All checks were successful
PR Check — Frontend / frontend (pull_request) Successful in 1m59s
- architecture.md: the Onboarding section describes the guided path as delivered (provider and useOnboarding(), the data-change channel of ADR 0022, the three onboarding preferences, rule D9, the pure models of onboardingPath.ts, the banner, the card latch and CARD_PAGES, the step cards, the highlight by props and CSS); the utils count and the user_preferences row follow the stack. - qa-onboarding.html: the phase 1 cases that still hold, without the compact bar ones, and four guided-path groups, including the manual checks the five links left for a release build (WebKitGTK and WebView2, Orca and NVDA, reduced motion, keyboard, Free edition, rule D9). - CHANGELOG (EN and FR): one entry for what every step card does, one per page, the path hidden on active profiles covers the banner too, the skipped state, and the guide's new sub-section. - STATE.md: manual checks due on the next release. Resolves #401 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
495 lines
32 KiB
HTML
495 lines
32 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>QA — Parcours d'accueil</title>
|
|
<!--
|
|
Checklist de parcours de l'accueil : phase 1 (issues #351 à #357) et parcours
|
|
guidé de la phase 2 (issues #396 à #401). Page statique, français seulement,
|
|
aucune dépendance réseau : tout est inline. L'état (cases + commentaires) vit
|
|
dans le localStorage du navigateur qui ouvre le fichier, sous une clé propre à
|
|
cette version de la liste ; « Copier le rapport » produit le texte brut à
|
|
coller dans Paramètres → Système → Envoyer un feedback.
|
|
La liste de la phase 1 telle que publiée avec la v0.17.0 reste lisible par
|
|
`git show v0.17.0:docs/qa-onboarding.html`.
|
|
-->
|
|
<style>
|
|
:root {
|
|
--bg: #faf9f7;
|
|
--card: #ffffff;
|
|
--fg: #1f1d1a;
|
|
--muted: #6b665f;
|
|
--border: #e3dfd9;
|
|
--accent: #b5542e;
|
|
--accent-fg: #ffffff;
|
|
--done-bg: #f0f5ec;
|
|
--focus: #2f6fb3;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #1c1a18;
|
|
--card: #262320;
|
|
--fg: #ece8e2;
|
|
--muted: #a39d94;
|
|
--border: #3a3631;
|
|
--accent: #d9774c;
|
|
--accent-fg: #1c1a18;
|
|
--done-bg: #262d22;
|
|
--focus: #7fb0e8;
|
|
}
|
|
}
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; padding: 0; }
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
padding: 24px 16px 64px;
|
|
}
|
|
main { max-width: 860px; margin: 0 auto; }
|
|
h1 { font-size: 1.5rem; margin: 0 0 4px; }
|
|
h2 { font-size: 1.05rem; margin: 28px 0 8px; }
|
|
p { margin: 0 0 10px; }
|
|
.lead { color: var(--muted); }
|
|
.meta {
|
|
display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center;
|
|
margin: 16px 0 8px; color: var(--muted); font-size: 0.9rem;
|
|
}
|
|
.meta label { display: inline-flex; gap: 6px; align-items: center; }
|
|
.meta input[type="text"] {
|
|
width: 11rem;
|
|
}
|
|
.progress {
|
|
font-variant-numeric: tabular-nums; font-weight: 600; color: var(--fg);
|
|
}
|
|
.prereq {
|
|
background: var(--card); border: 1px solid var(--border); border-radius: 10px;
|
|
padding: 12px 16px; margin: 12px 0 20px;
|
|
}
|
|
.prereq ul { margin: 6px 0 0; padding-left: 20px; }
|
|
.intro { color: var(--muted); font-size: 0.92rem; }
|
|
ol.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
|
|
ol.steps li {
|
|
background: var(--card); border: 1px solid var(--border); border-radius: 10px;
|
|
padding: 12px 14px; margin-bottom: 10px;
|
|
display: grid; grid-template-columns: auto 1fr; gap: 6px 12px;
|
|
}
|
|
ol.steps li.done { background: var(--done-bg); }
|
|
ol.steps li .check { grid-row: 1 / span 3; padding-top: 2px; }
|
|
ol.steps li input[type="checkbox"] { width: 20px; height: 20px; margin: 0; cursor: pointer; }
|
|
ol.steps li .title { font-weight: 600; cursor: pointer; }
|
|
ol.steps li .title::before {
|
|
counter-increment: step; content: counter(step) ". ";
|
|
color: var(--muted); font-weight: 500;
|
|
}
|
|
ol.steps li .expect { color: var(--muted); font-size: 0.92rem; }
|
|
ol.steps li .expect code {
|
|
font: 0.88em ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 0 4px;
|
|
}
|
|
ol.steps li .where { display: grid; gap: 4px; }
|
|
ol.steps li .where label { font-size: 0.85rem; color: var(--muted); }
|
|
input[type="text"], textarea {
|
|
width: 100%; font: inherit; color: var(--fg); background: var(--bg);
|
|
border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px;
|
|
}
|
|
input:focus-visible, textarea:focus-visible, button:focus-visible {
|
|
outline: 2px solid var(--focus); outline-offset: 1px;
|
|
}
|
|
.actions {
|
|
display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
|
|
margin-top: 20px; position: sticky; bottom: 0; padding: 12px 0;
|
|
background: var(--bg);
|
|
}
|
|
button {
|
|
font: inherit; font-weight: 600; cursor: pointer; border-radius: 8px;
|
|
padding: 8px 14px; border: 1px solid var(--border);
|
|
background: var(--card); color: var(--fg);
|
|
}
|
|
button.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
|
|
button:hover { filter: brightness(0.97); }
|
|
.status { color: var(--muted); font-size: 0.9rem; min-height: 1.4em; }
|
|
.fallback { margin-top: 12px; }
|
|
.fallback[hidden] { display: none; }
|
|
.fallback textarea { min-height: 14rem; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.85rem; }
|
|
.note { font-size: 0.85rem; color: var(--muted); margin-top: 16px; }
|
|
@media (max-width: 520px) {
|
|
body { padding: 16px 16px 72px; }
|
|
.meta input[type="text"] { width: 100%; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>QA — Parcours d'accueil</h1>
|
|
<p class="lead">Première ouverture de Simpl'Résultat jusqu'à la fin du parcours de démarrage guidé. Coche chaque étape quand elle se passe comme décrit, et note où tu as hésité, même une seconde : c'est l'hésitation qu'on cherche, pas l'échec.</p>
|
|
|
|
<div class="meta">
|
|
<label>Testeur <input type="text" id="tester" placeholder="prénom" autocomplete="off"></label>
|
|
<label>Version <input type="text" id="version" placeholder="ex. 0.18.0" autocomplete="off"></label>
|
|
<label>Système <input type="text" id="os" placeholder="Windows 11, Ubuntu 24.04…" autocomplete="off"></label>
|
|
<span class="progress" id="progress"></span>
|
|
</div>
|
|
|
|
<section class="prereq">
|
|
<strong>Avant de commencer</strong>
|
|
<ul>
|
|
<li>Une version publiée ou un build de release : les cas du parcours guidé vérifient le rendu réel du webview, WebKitGTK sous Linux et WebView2 sous Windows.</li>
|
|
<li>Une installation neuve, ou un profil neuf : la liste de démarrage n'apparaît que dans un profil sans transaction.</li>
|
|
<li>Pour l'étape Budget, une clé Base ou Premium ; sans clé, l'étape se présente verrouillée « Base et plus » et le total passe à 5 — note-le dans le champ, c'est attendu.</li>
|
|
<li>Pour le cas de l'édition Gratuite, une session utilisateur ou une machine sans clé : ne retire pas la clé de ta machine habituelle, puisque retirer une clé efface aussi le jeton d'activation.</li>
|
|
<li>Pour la règle D9, un profil qui a des transactions, créé avec la v0.17.0 ou avant, et qu'aucune version du parcours guidé n'a encore ouvert (un build de développement de la pile compte).</li>
|
|
<li>Les cas d'accessibilité et de rendu se font sous Linux avec Orca, puis sous Windows avec NVDA.</li>
|
|
<li>La page garde tes réponses dans ce navigateur. Rien n'est envoyé : c'est toi qui colles le rapport dans l'application.</li>
|
|
<li>Pour vérifier la v0.17.0 elle-même, prends la liste publiée avec elle : <code>git show v0.17.0:docs/qa-onboarding.html</code>.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<div id="sections"></div>
|
|
|
|
<div class="actions">
|
|
<button type="button" class="primary" id="copy">Copier le rapport</button>
|
|
<button type="button" id="reset">Réinitialiser</button>
|
|
<span class="status" id="status" role="status" aria-live="polite"></span>
|
|
</div>
|
|
|
|
<div class="fallback" id="fallback" hidden>
|
|
<p>Le presse-papiers n'est pas accessible depuis ce fichier : sélectionne le texte ci-dessous (il est déjà sélectionné) et copie-le avec Ctrl+C, puis colle-le dans Paramètres → Système → Envoyer un feedback.</p>
|
|
<textarea id="report" readonly aria-label="Rapport à copier"></textarea>
|
|
</div>
|
|
|
|
<p class="note">Les noms entre guillemets sont ceux des boutons tels qu'ils apparaissent en français. « Exemple Desjardins » et « Exemple Tangerine » sont des relevés inventés : aucune donnée réelle.</p>
|
|
</main>
|
|
|
|
<script>
|
|
(function () {
|
|
"use strict";
|
|
|
|
// v2: the guided path (#396 to #401) changed the list; the answers of the
|
|
// phase 1 list stay under their own key.
|
|
var STORAGE_KEY = "simpl-resultat.qa-onboarding.v2";
|
|
|
|
// One group per part of the path, one row per case. `expect` is what the
|
|
// tester should see. Ids are unique across the groups.
|
|
var SECTIONS = [
|
|
{
|
|
id: "phase-1",
|
|
title: "Phase 1 : premier lancement, états vides et dossier d'exemple",
|
|
intro: "Les cas de la phase 1 (#351 à #357) qui restent valides avec le parcours guidé. Ils peuvent se faire dans la même passe que la section suivante, sur le même profil neuf.",
|
|
steps: [
|
|
{ id: "install", title: "Installer et lancer l'application",
|
|
expect: "L'installateur s'exécute sans avertissement bloquant et l'application s'ouvre sur la sélection de profil." },
|
|
{ id: "profile", title: "Créer un profil",
|
|
expect: "Le profil s'ouvre directement après sa création (NIP facultatif)." },
|
|
{ id: "dashboard-empty", title: "Tableau de bord vide avec la liste de démarrage à 1 / 6",
|
|
expect: "Une carte d'accueil remplace les graphiques, la liste montre six lignes, seule « Créer un profil » est cochée, le compteur indique 1 / 6 (ou 1 / 5 sans clé)." },
|
|
{ id: "cta-folder", title: "Cliquer « Choisir un dossier d'import »",
|
|
expect: "Le bouton de la carte d'accueil mène à la page Import." },
|
|
{ id: "import-no-folder", title: "Page Import sans dossier",
|
|
expect: "Un état vide « Aucun dossier d'import » avec le bouton « Choisir un dossier » et le lien « Comment exporter un CSV depuis ma banque ? »." },
|
|
{ id: "pick-folder", title: "Cliquer « Choisir un dossier » et prendre un dossier vide",
|
|
expect: "La fenêtre du système s'ouvre ; une fois le dossier choisi, l'état vide devient « Aucune source trouvée » et nomme le dossier." },
|
|
{ id: "create-sample", title: "Cliquer « Créer un dossier d'exemple »",
|
|
expect: "Un message annonce les deux dossiers créés et invite à cliquer sur l'un d'eux. Dans l'explorateur de fichiers, les deux sous-dossiers contiennent un fichier releve-AAAA-MM.csv." },
|
|
{ id: "two-sources", title: "Deux sources apparaissent",
|
|
expect: "« Exemple Desjardins » et « Exemple Tangerine » sont listées comme sources, sans actualiser à la main." },
|
|
{ id: "import-desjardins", title: "Importer « Exemple Desjardins » : aperçu, signes, doublons, rapport",
|
|
expect: "Le format est détecté (bandeau Desjardins), l'aperçu montre des sorties et des entrées dans le bon sens (pas besoin d'« Inverser les signes »), aucun doublon, puis le rapport d'import indique le nombre de transactions importées." },
|
|
{ id: "manual-category", title: "Transactions : classer « GARDERIE LES PETITS PAS » à la main",
|
|
expect: "La transaction est sans catégorie après l'import ; lui en donner une à la main la garde classée." },
|
|
{ id: "budget", title: "Budget (clé Base ou Premium) : saisir un montant",
|
|
expect: "La page Budget montre la grille (pas l'état vide, puisqu'il y a des transactions) ; un montant saisi dans une cellule est conservé." },
|
|
{ id: "hide-reshow", title: "« Masquer » sur un second profil, puis réaffichage depuis Paramètres",
|
|
expect: "Créer un second profil (clé Base ou plus) : sa carte d'accueil montre la liste à 1 / 6. « Masquer » retire la liste de ce profil seulement, ainsi que le bandeau de ses autres pages ; le premier profil n'est pas touché. Paramètres → Utilisateurs montre « Afficher la liste de démarrage », qui ramène le parcours." },
|
|
{ id: "guide-link", title: "Ouvrir le guide « Exporter un CSV depuis votre banque » depuis l'état vide d'Import",
|
|
expect: "Sur un profil sans dossier ou sans source, le lien de l'état vide ouvre le guide directement sur la sous-section, avec les procédures par banque." }
|
|
]
|
|
},
|
|
{
|
|
id: "parcours-guide",
|
|
title: "Parcours guidé : un profil neuf de bout en bout",
|
|
intro: "Phase 2 (#396 à #401), sur un profil neuf en édition Base ou Premium. De l'étape 2 à l'étape 6, avance seulement par les liens d'étape (ligne de la liste, « Étape suivante »), sans revenir au tableau de bord : chaque page doit dire quoi faire.",
|
|
steps: [
|
|
{ id: "guided-dashboard", title: "Tableau de bord vide sans bandeau, autres pages avec bandeau",
|
|
expect: "Sur le tableau de bord, aucun bandeau de démarrage au-dessus du titre : la carte d'accueil porte déjà la liste. Ouvre Catégories, puis Rapports : chacune commence par le bandeau « Démarrage 1/6 · Étape suivante : Choisir un dossier d'import → », avec « Voir la liste » et « Masquer »." },
|
|
{ id: "guided-import-card", title: "Étape 2 : la carte Import, son contour et le focus",
|
|
expect: "Suis le lien « Choisir un dossier d'import » du bandeau ou de la liste. La page Import s'ouvre sur la carte « Étape 2 sur 6 · Choisir un dossier d'import », avant le titre et à la place du bandeau, et le focus du clavier est sur le titre de la carte. La phrase nomme le bouton « Choisir un dossier », entouré d'un contour de couleur d'accent qui pulse trois fois, puis reste." },
|
|
{ id: "guided-import-folder", title: "Étape 3 : dossier choisi, puis dossier d'exemple",
|
|
expect: "Choisis un dossier vide : sans quitter la page, la carte passe à « Étape 3 sur 6 · Configurer une source d'import », nomme le dossier et entoure « Créer un dossier d'exemple ». Après la création, elle demande de cliquer sur la carte de ta banque et entoure la première source." },
|
|
{ id: "guided-import-wizard", title: "Étape 4 : l'assistant, écran par écran",
|
|
expect: "Clique la source entourée (« Exemple Desjardins »). À chaque écran, la carte dit quoi vérifier et entoure le bouton suivant : « Aperçu », « Vérifier les doublons », « Confirmer », puis « Importer ». Pendant l'import, elle affiche « Import en cours… ». Sur le rapport, elle annonce « Fait. » et propose « Étape suivante : Classer une transaction à la main → » ; après « Terminé », elle garde « Fait. »." },
|
|
{ id: "guided-transactions", title: "Étape 5 : Transactions, entrée guidée",
|
|
expect: "Clique « Étape suivante » sur la carte Import. La page Transactions s'ouvre filtrée sur « Non catégorisées », période « Tout », avec la carte « Étape 5 sur 6 · Classer une transaction à la main » et le focus sur son titre ; le contour entoure la catégorie de la première ligne. Choisis une catégorie pour l'une d'elles (« GARDERIE LES PETITS PAS », par exemple) : sans quitter la page, la carte annonce « Fait. » et propose « Étape suivante : Saisir un premier budget → », et le contour disparaît." },
|
|
{ id: "guided-budget", title: "Étape 6 : Budget, « Tout déplier » puis la cellule du mois",
|
|
expect: "Clique « Étape suivante » sur la carte Transactions. La page Budget montre la carte « Étape 6 sur 6 · Saisir un premier budget », la grille repliée et le contour sur « Tout déplier ». Clique-le : la phrase change et le contour passe sur la cellule du mois courant de la première catégorie à remplir. Saisis un montant puis Entrée : la carte annonce « Fait. », puis le message de fin, avec « Ouvrir le guide » et « Fermer »." },
|
|
{ id: "guided-budget-tab", title: "Budget : Tab enregistre et passe au mois suivant",
|
|
expect: "Dans une cellule, saisis un montant puis Tab : le montant est enregistré et la saisie passe au mois suivant, comme le dit la carte." },
|
|
{ id: "guided-live", title: "Les étapes se cochent page ouverte",
|
|
expect: "Pendant les étapes 2 à 6, chaque étape s'est cochée sur la page même, sans recharger ni changer de page, en moins d'une seconde : passage de l'étape 2 à l'étape 3 au choix du dossier, puis « Fait. » après l'import, le classement et le budget. Note tout délai visible, ou une étape qui ne se coche qu'au changement de page." },
|
|
{ id: "guided-end", title: "Fin du parcours : bandeau de fin, « Ouvrir le guide » et « Fermer »",
|
|
expect: "Quitte la page Budget : en haut de chaque page, tableau de bord compris, le bandeau dit « Démarrage terminé. Le guide d'utilisation vous accompagne pour la suite. ». « Ouvrir le guide » ouvre Paramètres → Utilisateurs sur la section « Premiers pas », qui décrit le parcours (« Le parcours de démarrage »). « Fermer » retire le bandeau de toutes les pages, et Paramètres → Utilisateurs propose alors « Afficher la liste de démarrage »." }
|
|
]
|
|
},
|
|
{
|
|
id: "parcours-guide-liste",
|
|
title: "Parcours guidé : bandeau, liste, Passer et Reprendre",
|
|
intro: "Sur un profil où le parcours n'est pas terminé, par exemple un second profil neuf (clé Base ou plus).",
|
|
steps: [
|
|
{ id: "guided-list", title: "« Voir la liste » et « Fermer la liste »",
|
|
expect: "Sur une page sans carte (Catégories, par exemple), « Voir la liste » déplie les six étapes sous le bandeau et devient « Fermer la liste », qui les replie. Change de page : la liste est de nouveau repliée. Une étape faite est barrée et sans lien ; une étape à faire mène à sa page." },
|
|
{ id: "guided-skip-resume", title: "« Passer cette étape », puis « Reprendre »",
|
|
expect: "Sur la carte Transactions ou Budget, clique « Passer cette étape » : la carte laisse la place au bandeau (ou au message de fin si c'était la dernière étape) et le compteur avance d'une étape. Sur la page Import, passer une étape fait passer la carte à l'étape suivante de la page. Dans la liste dépliée, l'étape passée montre l'icône « passer », le badge « Passée » et « Reprendre ». Depuis une autre page, « Reprendre » rouvre la page de l'étape avec sa carte, le focus sur son titre, et l'étape redevient à faire." },
|
|
{ id: "guided-hide-users", title: "« Masquer » depuis Paramètres → Utilisateurs",
|
|
expect: "Sur Paramètres → Utilisateurs, clique « Masquer » dans le bandeau : le bandeau disparaît et, sans quitter la page, la ligne « Parcours de démarrage » apparaît avec « Afficher la liste de démarrage ». Un clic la remplace par « Le parcours de démarrage réapparaît en haut des pages », et le bandeau revient en haut de la page." }
|
|
]
|
|
},
|
|
{
|
|
id: "parcours-guide-cas",
|
|
title: "Parcours guidé : cas particuliers et limites connues",
|
|
intro: "Chaque cas dit sur quel profil le faire. Les limites connues sont à constater et à juger : note si elles gênent.",
|
|
steps: [
|
|
{ id: "guided-d9", title: "Profil déjà actif masqué d'office, puis réaffiché (règle D9)",
|
|
expect: "Ouvre un profil qui avait des transactions avant cette version. Aucun bandeau ni carte, sur aucune page ; le Journal (Paramètres → Système) porte « Onboarding: the profile already had transactions, getting-started path hidden (Settings → Users shows it again) ». Paramètres → Utilisateurs propose « Afficher la liste de démarrage » : un clic ramène le bandeau, avec un compteur fidèle aux données du profil. Rouvrir le profil ne le masque plus." },
|
|
{ id: "guided-free", title: "Édition Gratuite : 5 étapes, fin à 5/5",
|
|
expect: "Sur une session ou une machine sans clé, avec un profil neuf : le compteur dit 1/5, et la ligne Budget est verrouillée « Base et plus » et mène à la carte Licence. Après le classement d'une transaction, la carte Transactions (« Étape 5 sur 5 ») annonce « Fait. » puis le message de fin : aucune carte Budget, fin à 5/5." },
|
|
{ id: "guided-noop-import", title: "Un import qui n'ajoute rien",
|
|
expect: "Sur un profil de test où « Importer un premier fichier » reste à faire, fais un import qui n'ajoute aucune transaction (par exemple un fichier qui n'a que sa ligne d'en-tête, ou un format de date choisi à tort, qui met toutes les lignes en erreur dans l'aperçu). Sur le rapport, la carte dit « L'import n'a ajouté aucune transaction ; le rapport ci-dessous dit pourquoi. » et n'annonce pas « Fait. ». À juger : la liste coche quand même « Importer un premier fichier », parce qu'elle compte les fichiers importés et non les transactions, et la carte annonce « Fait. » après « Terminé »." },
|
|
{ id: "guided-fallback", title: "Transactions : aucune transaction à classer",
|
|
expect: "Sur un profil de test où l'étape 5 reste à faire, rends toutes les transactions catégorisées sans les classer à la main : ajoute, depuis la page Catégories, un mot-clé pour chaque transaction restée sans catégorie, puis clique « Auto-catégoriser » sur la page Transactions. Depuis une autre page, suis « Étape suivante : Classer une transaction à la main → ». La page montre toutes les transactions, période « Tout », une seule fois, et la carte dit « Cliquez sur la catégorie d'une transaction… ». « Aucune transaction trouvée. » peut s'afficher un très bref instant entre les deux lectures (limite connue)." },
|
|
{ id: "guided-same-page", title: "Limite connue : un lien d'étape suivi depuis la page Transactions",
|
|
expect: "Passe l'étape 5 depuis sa carte, change les filtres de la page Transactions, puis, sur cette même page, clique « Reprendre » dans la liste du bandeau. La carte revient, mais la page garde tes filtres au lieu de l'entrée guidée ; la phrase de la carte suit le filtre réellement affiché. À juger." },
|
|
{ id: "guided-budget-expanded", title: "Budget : grille déjà dépliée par le profil",
|
|
expect: "Sur un profil où l'étape 6 reste à faire, déplie la grille (« Tout déplier ») sans saisir de montant, puis quitte la page Budget. Reviens par le lien de l'étape 6 : la grille est restée dépliée et la carte entoure directement la cellule du mois courant." },
|
|
{ id: "guided-budget-offscreen", title: "Limite connue : cellule du mois hors de la zone visible",
|
|
expect: "En fenêtre étroite, la grille du budget défile horizontalement et la cellule du mois courant peut être hors de vue (octobre est vers la droite) : rien ne défile tout seul. À juger : la phrase et le contour suffisent-ils pour la trouver ?" },
|
|
{ id: "guided-budget-zero", title: "Budget : une cellule quittée vide enregistre 0 $",
|
|
expect: "Sur un profil où l'étape 6 reste à faire, clique une cellule puis quitte-la sans rien saisir : 0 $ est enregistré et la carte annonce « Fait. ». C'est le comportement existant de la grille ; à juger." },
|
|
{ id: "guided-import-flash", title: "Limite connue : « Aucun dossier configuré » à l'ouverture d'Import",
|
|
expect: "À l'ouverture de la page Import, la carte du dossier peut afficher un très bref instant « Aucun dossier configuré » avant la lecture du dossier ; l'état vide et la carte de démarrage, eux, attendent cette lecture. À juger." }
|
|
]
|
|
},
|
|
{
|
|
id: "parcours-guide-a11y",
|
|
title: "Parcours guidé : accessibilité et rendu",
|
|
intro: "Sous Linux (WebKitGTK, lecteur d'écran Orca), puis sous Windows (WebView2, lecteur d'écran NVDA). Indique le système dans le champ prévu.",
|
|
steps: [
|
|
{ id: "guided-render-linux", title: "Contour et pulsation sous Linux (WebKitGTK)",
|
|
expect: "Un contour de couleur d'accent, d'environ 3 px et décalé de 2 px, entoure le contrôle désigné sur les trois pages ; la pulsation joue trois fois en un peu moins de 4 secondes, puis le contour reste fixe. Note s'il est rogné par la grille ou par un conteneur." },
|
|
{ id: "guided-render-windows", title: "Contour et pulsation sous Windows (WebView2)",
|
|
expect: "Même attendu que sous Linux : contour de couleur d'accent d'environ 3 px, décalé de 2 px, trois pulsations, puis un contour fixe, jamais rogné." },
|
|
{ id: "guided-reduced-motion", title: "Mouvement réduit (prefers-reduced-motion)",
|
|
expect: "Active l'option du système qui réduit les animations (GNOME : réduire les animations, dans Accessibilité ; Windows : effets d'animation désactivés, dans Accessibilité → Effets visuels), puis relance l'application si rien ne change : le contour s'affiche, sans aucune pulsation." },
|
|
{ id: "guided-contrast", title: "Contraste du contour, thème clair et thème sombre",
|
|
expect: "Dans les deux thèmes (bouton de la barre latérale), le contour se distingue nettement du fond de la page, des cartes et du contour de focus du clavier, sur les pages Import, Transactions et Budget." },
|
|
{ id: "guided-keyboard", title: "Clavier seul",
|
|
expect: "Sans souris, de l'étape 2 à l'étape 6 : Tab atteint « Étape suivante », « Voir la liste », « Masquer », les lignes de la liste, « Passer cette étape » et le contrôle entouré, et Entrée ou Espace les active. À l'arrivée par un lien d'étape, le focus est sur le titre de la carte, et Tab repart de là vers le contrôle entouré. Le focus reste visible et n'est jamais pris au piège." },
|
|
{ id: "guided-orca", title: "Annonces d'Orca (Linux)",
|
|
expect: "Le bandeau et la carte s'annoncent comme la région « Parcours de démarrage » ; « Voir la liste » annonce son état replié ou déplié ; dans la liste, chaque étape est suivie de son état (« faite », « passée », « verrouillée », « en attente »), sauf une étape à faire ; à l'arrivée par « Étape suivante », le titre de la carte est lu ; « Fait. » est annoncé sans déplacer le focus ; la confirmation de Paramètres → Utilisateurs est annoncée." },
|
|
{ id: "guided-nvda", title: "Annonces de NVDA (Windows)",
|
|
expect: "Mêmes annonces que sous Orca : région « Parcours de démarrage », état replié ou déplié de « Voir la liste », état de chaque étape de la liste, titre de la carte à l'arrivée par « Étape suivante », « Fait. » sans déplacement du focus, confirmation de Paramètres → Utilisateurs." }
|
|
]
|
|
}
|
|
];
|
|
|
|
var ALL_STEPS = [];
|
|
SECTIONS.forEach(function (section) {
|
|
section.steps.forEach(function (step) { ALL_STEPS.push(step); });
|
|
});
|
|
|
|
var state = load();
|
|
|
|
function load() {
|
|
var fresh = { meta: { tester: "", version: "", os: "" }, steps: {} };
|
|
try {
|
|
var raw = window.localStorage.getItem(STORAGE_KEY);
|
|
if (!raw) return fresh;
|
|
var parsed = JSON.parse(raw);
|
|
if (!parsed || typeof parsed !== "object") return fresh;
|
|
return {
|
|
meta: Object.assign(fresh.meta, parsed.meta || {}),
|
|
steps: parsed.steps && typeof parsed.steps === "object" ? parsed.steps : {}
|
|
};
|
|
} catch (e) {
|
|
return fresh;
|
|
}
|
|
}
|
|
|
|
function save() {
|
|
try {
|
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
} catch (e) {
|
|
setStatus("Impossible d'enregistrer dans ce navigateur : le rapport reste copiable, mais l'état sera perdu au rechargement.");
|
|
}
|
|
}
|
|
|
|
function stepState(id) {
|
|
if (!state.steps[id]) state.steps[id] = { done: false, note: "" };
|
|
return state.steps[id];
|
|
}
|
|
|
|
function setStatus(text) {
|
|
document.getElementById("status").textContent = text;
|
|
}
|
|
|
|
function countDone() {
|
|
var done = 0;
|
|
ALL_STEPS.forEach(function (s) { if (stepState(s.id).done) done += 1; });
|
|
return done;
|
|
}
|
|
|
|
function updateProgress() {
|
|
document.getElementById("progress").textContent = countDone() + " / " + ALL_STEPS.length + " étapes cochées";
|
|
}
|
|
|
|
function renderStep(step, list) {
|
|
var st = stepState(step.id);
|
|
var li = document.createElement("li");
|
|
li.className = st.done ? "done" : "";
|
|
|
|
var check = document.createElement("div");
|
|
check.className = "check";
|
|
var box = document.createElement("input");
|
|
box.type = "checkbox";
|
|
box.id = "done-" + step.id;
|
|
box.checked = !!st.done;
|
|
box.addEventListener("change", function () {
|
|
st.done = box.checked;
|
|
li.className = st.done ? "done" : "";
|
|
save();
|
|
updateProgress();
|
|
});
|
|
check.appendChild(box);
|
|
|
|
var title = document.createElement("label");
|
|
title.className = "title";
|
|
title.htmlFor = box.id;
|
|
title.textContent = step.title;
|
|
|
|
var expect = document.createElement("div");
|
|
expect.className = "expect";
|
|
expect.textContent = step.expect;
|
|
|
|
var where = document.createElement("div");
|
|
where.className = "where";
|
|
var noteLabel = document.createElement("label");
|
|
noteLabel.htmlFor = "note-" + step.id;
|
|
noteLabel.textContent = "Où as-tu hésité ?";
|
|
var note = document.createElement("input");
|
|
note.type = "text";
|
|
note.id = noteLabel.htmlFor;
|
|
note.value = st.note || "";
|
|
note.autocomplete = "off";
|
|
note.addEventListener("input", function () {
|
|
st.note = note.value;
|
|
save();
|
|
});
|
|
where.appendChild(noteLabel);
|
|
where.appendChild(note);
|
|
|
|
li.appendChild(check);
|
|
li.appendChild(title);
|
|
li.appendChild(expect);
|
|
li.appendChild(where);
|
|
list.appendChild(li);
|
|
}
|
|
|
|
function render() {
|
|
var root = document.getElementById("sections");
|
|
root.textContent = "";
|
|
SECTIONS.forEach(function (section) {
|
|
var block = document.createElement("section");
|
|
block.id = section.id;
|
|
|
|
var heading = document.createElement("h2");
|
|
heading.textContent = section.title;
|
|
block.appendChild(heading);
|
|
|
|
var intro = document.createElement("p");
|
|
intro.className = "intro";
|
|
intro.textContent = section.intro;
|
|
block.appendChild(intro);
|
|
|
|
var list = document.createElement("ol");
|
|
list.className = "steps";
|
|
section.steps.forEach(function (step) { renderStep(step, list); });
|
|
block.appendChild(list);
|
|
|
|
root.appendChild(block);
|
|
});
|
|
|
|
["tester", "version", "os"].forEach(function (key) {
|
|
var input = document.getElementById(key);
|
|
input.value = state.meta[key] || "";
|
|
input.oninput = function () {
|
|
state.meta[key] = input.value;
|
|
save();
|
|
};
|
|
});
|
|
|
|
updateProgress();
|
|
}
|
|
|
|
function pad(n) { return n < 10 ? "0" + n : String(n); }
|
|
|
|
function buildReport() {
|
|
var now = new Date();
|
|
var date = now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate()) +
|
|
" " + pad(now.getHours()) + ":" + pad(now.getMinutes());
|
|
var lines = [];
|
|
lines.push("QA parcours d'accueil — " + date);
|
|
if (state.meta.tester) lines.push("Testeur : " + state.meta.tester);
|
|
if (state.meta.version) lines.push("Version : " + state.meta.version);
|
|
if (state.meta.os) lines.push("Système : " + state.meta.os);
|
|
lines.push("Étapes cochées : " + countDone() + " / " + ALL_STEPS.length);
|
|
SECTIONS.forEach(function (section) {
|
|
lines.push("");
|
|
lines.push("== " + section.title + " ==");
|
|
section.steps.forEach(function (step, i) {
|
|
var st = stepState(step.id);
|
|
lines.push((st.done ? "[x] " : "[ ] ") + (i + 1) + ". " + step.title);
|
|
if (st.note && st.note.trim()) lines.push(" Hésitation : " + st.note.trim());
|
|
});
|
|
});
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function showFallback(text) {
|
|
var box = document.getElementById("fallback");
|
|
var area = document.getElementById("report");
|
|
area.value = text;
|
|
box.hidden = false;
|
|
area.focus();
|
|
area.select();
|
|
}
|
|
|
|
document.getElementById("copy").addEventListener("click", function () {
|
|
var text = buildReport();
|
|
document.getElementById("fallback").hidden = true;
|
|
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
navigator.clipboard.writeText(text).then(function () {
|
|
setStatus("Rapport copié. Colle-le dans Paramètres → Système → Envoyer un feedback.");
|
|
}, function () {
|
|
setStatus("Copie automatique refusée : utilise la zone ci-dessous.");
|
|
showFallback(text);
|
|
});
|
|
} else {
|
|
setStatus("Copie automatique indisponible : utilise la zone ci-dessous.");
|
|
showFallback(text);
|
|
}
|
|
});
|
|
|
|
document.getElementById("reset").addEventListener("click", function () {
|
|
if (!window.confirm("Effacer toutes les cases et tous les commentaires ?")) return;
|
|
state = { meta: { tester: "", version: "", os: "" }, steps: {} };
|
|
try { window.localStorage.removeItem(STORAGE_KEY); } catch (e) { /* nothing to remove */ }
|
|
document.getElementById("fallback").hidden = true;
|
|
setStatus("Checklist réinitialisée.");
|
|
render();
|
|
});
|
|
|
|
render();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|