docs: capitalize the mairie form prefill and config solution (T1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
23e58b17ee
commit
ee79ca0757
+161
@@ -0,0 +1,161 @@
|
||||
# Solution : [T1] Content script : pré-remplir le formulaire mairie (+ config)
|
||||
|
||||
## Problème résolu
|
||||
|
||||
Au clic sur « Créer sur le site mairie », l'extension déposait l'événement dans
|
||||
`storage.local` puis ouvrait un **formulaire vide** : le bénévole devait recopier
|
||||
à la main titre, dates, heures, adresse, organisateur, email — exactement le
|
||||
geste que l'extension existe pour supprimer. La boucle n'était pas fermée.
|
||||
|
||||
Objectif tenu : le formulaire s'ouvre **déjà rempli** de ce que la machine sait,
|
||||
**laisse vide** ce qu'elle ignore (jamais de valeur inventée), et **dit** dans un
|
||||
bandeau ce qui reste à compléter. Le bénévole relit, complète (image, thème,
|
||||
enrichissement) et envoie lui-même — **pas d'auto-submit**.
|
||||
|
||||
Ce ticket a aussi **introduit la config** de l'extension.
|
||||
|
||||
## Approche choisie
|
||||
|
||||
**Deux content scripts classiques partageant un scope + namespace `globalThis`**
|
||||
(approche 3 du brainstorm), **+ config à défauts committés et surcharge locale
|
||||
optionnelle** (option (a)).
|
||||
|
||||
Le vrai risque du ticket n'était pas « poser des valeurs dans un DOM » : c'était
|
||||
le **calcul des dates** (DTEND exclusif, dates flottantes de journée entière,
|
||||
franchissement de minuit) — invisible sous `TZ=Europe/Paris`, la famille de bugs
|
||||
qui avait déjà coûté deux passes au ticket précédent. Il fallait donc que ce
|
||||
calcul reste **sous `bun test`**.
|
||||
|
||||
Trois alternatives écartées, et pourquoi :
|
||||
- **Content script monolithique** : logique de dates intestable (rien à importer
|
||||
en `bun test`) → on coderait à l'aveugle les deux bugs les plus chers.
|
||||
- **Module ESM en import dynamique** (`import(runtime.getURL(...))` +
|
||||
`web_accessible_resources`) : s'appuie sur un **bug ouvert MV3 de Firefox**
|
||||
([bugzilla 1803950](https://bugzilla.mozilla.org/show_bug.cgi?id=1803950)) —
|
||||
parier le risque sur le point le plus fragile de la plateforme.
|
||||
- **Formater en amont, content script bête** : réviserait une **couture figée
|
||||
trois jours plus tôt** et coupleraient la page liste au formulaire mairie.
|
||||
|
||||
L'approche retenue garde le calcul testable **sans** parier sur un bug plateforme
|
||||
**ni** rouvrir une couture fraîche.
|
||||
|
||||
## Décisions clés
|
||||
|
||||
- **`config.js` sans `import` ni `export`, communique par `globalThis`.** Un
|
||||
fichier sans `import`/`export` est valide **à la fois** comme script classique
|
||||
(content script, background) **et** comme module ESM (`import "./config.js"`
|
||||
depuis `presentation.js` et `bun test`). C'est ce qui permet d'absorber
|
||||
`LIEU_PAR_DEFAUT` (fin d'une duplication) sans build. ⚠️ Le jour où quelqu'un
|
||||
ajoute un `export`, tous les content scripts cassent → acté dans `DECISIONS.md`,
|
||||
avec un commentaire en tête de fichier.
|
||||
|
||||
- **Le background lit `config.local.json`, pas le content script.** Un content
|
||||
script ne peut `fetch(runtime.getURL(...))` que si la ressource est en
|
||||
`web_accessible_resources` — ce qui **exposerait l'email à la page de la
|
||||
mairie**. Le background lit sans WAR et transmet via `storage.local`.
|
||||
|
||||
- **Config optionnelle, dégradation propre.** Fichier absent (clone frais), HTTP
|
||||
non-ok ou JSON invalide → `{}` → organisateur/email vides **signalés dans le
|
||||
bandeau**, tout le reste rempli. Un `config.local.json` **requis** (énoncé
|
||||
littéral) aurait cassé tout clone frais : rejeté. Le fichier n'étant pas
|
||||
garanti présent, il **ne peut pas** être listé dans le manifest (Firefox refuse
|
||||
d'installer si un fichier `js` manque) : `fetch` + repli est la seule voie.
|
||||
|
||||
- **`CLE_EVENEMENT_EN_ATTENTE` dupliquée + test de dérive.** La constante vit dans
|
||||
`transfert.js` (ESM), inatteignable depuis un content script classique.
|
||||
Dupliquée volontairement, avec un test qui garantit l'égalité avec l'originale
|
||||
(plutôt que rouvrir la couture pour convertir `transfert.js`).
|
||||
|
||||
- **Honnêteté machine/humain.** Jamais de « 00:00 » sur une journée entière
|
||||
(heures laissées vides), jamais de `lat|lng` inventé sur un lieu hors café,
|
||||
`input_6_35` (accessibilité) laissé intact, honeypots intacts. Tout manque
|
||||
remonte dans `aCompleter` (bandeau).
|
||||
|
||||
- **`config.local.json.example` dans `extension/`, pas à la racine.** Le
|
||||
background résout `runtime.getURL("config.local.json")` **dans `extension/`** :
|
||||
l'exemple doit être à côté de sa cible de copie.
|
||||
|
||||
## Patterns à réutiliser
|
||||
|
||||
- **Fichier « bilingue » ESM/classique** : ne poser `import`/`export` nulle part,
|
||||
publier l'API sur `globalThis.MonNamespace`. Chargeable partout dans une
|
||||
extension MV3 sans build. Le prix : une convention à documenter pour qu'un futur
|
||||
agent ne la « corrige » pas en `export`.
|
||||
|
||||
- **Séparer calcul pur (testé) / effet de bord (fin, non testé).**
|
||||
`calculerValeurs(payload, config)` est pur et renvoie un objet **plat, prêt à
|
||||
poser** (`dateDebut: "18/07/2026"`, `heureDebut: ["19","30"] | null`, …) ;
|
||||
`formulaire-mairie.js` ne fait que lire le storage et écrire le DOM. Le bug des
|
||||
heures (voir Pièges) a **validé** ce découpage : confiné au poseur, diagnostiqué
|
||||
en une pile d'appels, corrigé en quelques lignes, cœur testé jamais touché.
|
||||
|
||||
- **Merge tolérant pour config éditée à la main** : `fusionner(defauts, locale)`
|
||||
traite `null` / non-objet / tableau comme « pas de surcharge » et ne mute pas
|
||||
les défauts. Une config optionnelle ne doit **jamais** lever.
|
||||
|
||||
- **Dates de calendrier sans fuseau.** Journée entière = date flottante
|
||||
(RFC 5545) → lire **uniquement** les composantes locales
|
||||
(`getFullYear/getMonth/getDate`), jamais l'heure, jamais un fuseau. DTEND est
|
||||
**exclusif** → reculer la date de fin d'un jour (en composantes locales, jamais
|
||||
« −24h »). Événement horodaté → tout à `Europe/Paris` via `Intl.DateTimeFormat`,
|
||||
heures zéro-paddées. **Épingler `TZ=Europe/Paris` dans les tests ET rejouer sous
|
||||
plusieurs fuseaux** (le bug de fuseau est invisible sous le fuseau de dev).
|
||||
|
||||
- **Poser une valeur dans un champ piloté par un framework** : après `.value`,
|
||||
dispatcher `new Event("input", {bubbles:true})` **puis**
|
||||
`new Event("change", {bubbles:true})` (Gravity Forms écoute `change`). Le
|
||||
dispatch est gratuit — ne pas supposer que `.value` suffit.
|
||||
|
||||
- **Isoler chaque effet de bord dans son propre `try/catch`.** Un `try` global
|
||||
autour du remplissage laissait la première exception emporter **tous** les
|
||||
champs suivants. Un `try` **par champ** → le champ fautif part dans `manquants`,
|
||||
les autres sont posés. Ferme la classe entière de panne, pas le cas vu.
|
||||
|
||||
- **Rendre tout geste automatique visible.** Un bandeau injecté dit ce qui a été
|
||||
fait et ce qui reste ; storage vide (visite directe) → **rien**. Un
|
||||
préremplissage silencieusement partiel soumis à une mairie qui modère est le
|
||||
pire échec possible.
|
||||
|
||||
## Pièges à éviter
|
||||
|
||||
- 🔴 **Ne jamais déduire le type d'un champ de son intitulé — le HTML est la seule
|
||||
source de vérité.** L'énoncé du ticket affirmait « Heures = 2 **selects**
|
||||
input_6_38_1/_2 ». Le HTML réel dit `<input type="number">`. `poserSelect`
|
||||
faisait `[...champ.options]` sur un `<input>` → `champ.options` undefined →
|
||||
`TypeError`, qui (avant l'isolation par champ) vidait aussi Tarifs, adresse et
|
||||
coordonnées. Corrigé : heures via `poserTexte` (le zéro-padding `"09"`/`"00"`
|
||||
reste un *valid floating-point number* au sens HTML). `RECHERCHE.md` §2 a été
|
||||
corrigée à la racine (colonne « balise réelle » + avertissement) pour qu'un
|
||||
futur agent ne repaie pas ce bug. C'est la cause racine du seul bug du ticket,
|
||||
et il venait d'un **doc/énoncé faux cru sur parole**.
|
||||
|
||||
- **Toucher au `manifest.json` impose Décharger + Charger**, pas « Recharger » ni
|
||||
F5. Piège déjà payé plusieurs fois. Fermer aussi les anciens onglets de la liste
|
||||
(page orpheline = clics inertes).
|
||||
|
||||
- **DTEND exclusif** : sans recul d'un jour, la date de fin d'un événement
|
||||
multi-jours est **fausse** — et soumise à une mairie qui modère (personne ne
|
||||
relit une date qui *a l'air* juste). Le bug le plus cher du ticket.
|
||||
|
||||
- **Course background ↔ content script** (P3 ouvert, non corrigé) :
|
||||
`chargerConfigLocale()` est async ; au tout premier chargement post-install, si
|
||||
le formulaire s'ouvrait avant la fin du `fetch`, organisateur/email seraient
|
||||
vides à tort. Improbable (la valeur persiste ensuite), noté pour mémoire.
|
||||
|
||||
- **`chargerConfigLocale()` s'exécute à chaque réveil de l'event page** et écrit
|
||||
`{}` si le fichier est absent. Toute future page d'options qui écrirait dans
|
||||
`storage.local` verrait ses valeurs **écrasées par `{}`** au prochain réveil.
|
||||
Trancher « qui fait autorité, fichier ou UI » fait partie du futur ticket — ce
|
||||
n'est pas « juste ajouter un écran ».
|
||||
|
||||
- **Le poseur DOM n'est pas testé unitairement** (aucun harnais DOM dans le repo ;
|
||||
happy-dom n'émule pas le JS de Gravity Forms / Leaflet). C'est le **test humain**
|
||||
qui tranche le rendu réel (datepicker, marqueur carte, bandeau non écrasé par le
|
||||
CSS de la mairie). Le bug des heures l'a prouvé : seul le DOM réel tranche.
|
||||
|
||||
- **Config `cafe` partielle interdite** : `fusionner` est shallow → une locale
|
||||
avec un `cafe` incomplet produirait `"undefined|adresse"`. Le README précise que
|
||||
seuls `organisateur` et `email` sont surchargeables.
|
||||
|
||||
## Tags
|
||||
tags: [firefox-extension, manifest-v3, content-script, globalthis, esm, gravity-forms, dates, timezone, rfc5545, config, bun-test, tdd, vanilla-js]
|
||||
Reference in New Issue
Block a user