refactor(docs): réorganiser la documentation selon principes DDD
Réorganise la documentation du projet selon les principes du Domain-Driven Design (DDD) pour améliorer la cohésion, la maintenabilité et l'alignement avec l'architecture modulaire du backend. **Structure cible:** ``` docs/domains/ ├── README.md (Context Map) ├── _shared/ (Core Domain) ├── recommendation/ (Supporting Subdomain) ├── content/ (Supporting Subdomain) ├── moderation/ (Supporting Subdomain) ├── advertising/ (Generic Subdomain) ├── premium/ (Generic Subdomain) └── monetization/ (Generic Subdomain) ``` **Changements effectués:** Phase 1: Création de l'arborescence des 7 bounded contexts Phase 2: Déplacement des règles métier (01-19) vers domains/*/rules/ Phase 3: Déplacement des diagrammes d'entités vers domains/*/entities/ Phase 4: Déplacement des diagrammes flux/états/séquences vers domains/*/ Phase 5: Création des README.md pour chaque domaine Phase 6: Déplacement des features Gherkin vers domains/*/features/ Phase 7: Création du Context Map (domains/README.md) Phase 8: Mise à jour de mkdocs.yml pour la nouvelle navigation Phase 9: Correction automatique des liens internes (script fix-markdown-links.sh) Phase 10: Nettoyage de l'ancienne structure (regles-metier/, diagrammes/, features/) **Configuration des tests:** - Makefile: godog run docs/domains/*/features/ - scripts/generate-bdd-docs.py: features_dir → docs/domains **Avantages:** ✅ Cohésion forte: toute la doc d'un domaine au même endroit ✅ Couplage faible: domaines indépendants, dépendances explicites ✅ Navigabilité améliorée: README par domaine = entrée claire ✅ Alignement code/docs: miroir de backend/internal/ ✅ Onboarding facilité: exploration domaine par domaine ✅ Tests BDD intégrés: features au plus près des règles métier Voir docs/REFACTOR-DDD.md pour le plan complet.
This commit is contained in:
472
docs/domains/recommendation/features/recherche/recherche.feature
Normal file
472
docs/domains/recommendation/features/recherche/recherche.feature
Normal file
@@ -0,0 +1,472 @@
|
||||
# language: fr
|
||||
|
||||
Fonctionnalité: Recherche de contenu
|
||||
En tant qu'utilisateur de RoadWave
|
||||
Je veux rechercher des contenus audio par mots-clés, localisation et filtres
|
||||
Afin de trouver facilement le contenu qui m'intéresse
|
||||
|
||||
Contexte:
|
||||
Étant donné que l'application RoadWave est démarrée
|
||||
Et que l'utilisateur "jean@example.com" est connecté
|
||||
|
||||
# 15.3.1 - Recherche par mot-clé
|
||||
|
||||
Scénario: Recherche full-text basique
|
||||
Étant donné que la base contient les contenus suivants:
|
||||
| titre | description | créateur |
|
||||
| Balade à Paris | Visite du quartier Latin | @paris_stories |
|
||||
| Secrets de Montmartre | Histoire de la butte | @explore_paris |
|
||||
| Voyage en Normandie | Découverte des plages | @voyages_fr |
|
||||
Quand l'utilisateur recherche "paris"
|
||||
Alors 2 résultats sont retournés
|
||||
Et les résultats incluent "Balade à Paris"
|
||||
Et les résultats incluent "Secrets de Montmartre"
|
||||
|
||||
Scénario: Recherche avec stemming français
|
||||
Étant donné un contenu avec le titre "Voyage en Bretagne"
|
||||
Quand l'utilisateur recherche "voyages"
|
||||
Alors le contenu "Voyage en Bretagne" est trouvé
|
||||
Et le stemming a transformé "voyages" en racine "voyag"
|
||||
|
||||
Plan du Scénario: Stemming français sur différentes formes
|
||||
Étant donné un contenu avec le mot "<mot_original>"
|
||||
Quand l'utilisateur recherche "<recherche>"
|
||||
Alors le contenu est trouvé grâce au stemming français
|
||||
|
||||
Exemples:
|
||||
| mot_original | recherche |
|
||||
| voyage | voyages |
|
||||
| voyager | voyage |
|
||||
| balades | balade |
|
||||
| historique | histoire |
|
||||
|
||||
Scénario: Recherche avec accents ignorés
|
||||
Étant donné un contenu avec le titre "Découverte de l'Élysée"
|
||||
Quand l'utilisateur recherche "decouverte elysee"
|
||||
Alors le contenu est trouvé
|
||||
Et les accents sont normalisés automatiquement
|
||||
|
||||
Scénario: Champs indexés avec pondération
|
||||
Étant donné les contenus suivants:
|
||||
| titre | description | créateur | tags |
|
||||
| Voyage Paris | Balade sympa | @user1 | Tourisme |
|
||||
| Balade Lyon | Voyage en ville | @paris_guide | Voyage |
|
||||
Quand l'utilisateur recherche "paris"
|
||||
Alors "Voyage Paris" est en première position
|
||||
Parce que le titre a un poids × 3
|
||||
Et "@paris_guide" apparaît en second
|
||||
Parce que le créateur a un poids × 2
|
||||
|
||||
Scénario: Ranking par pertinence et popularité
|
||||
Étant donné les contenus suivants:
|
||||
| titre | écoutes | rang_texte |
|
||||
| Balade Paris | 50000 | 0.8 |
|
||||
| Paris la nuit | 1000 | 0.9 |
|
||||
Quand l'utilisateur recherche "paris"
|
||||
Alors le score final combine rang_texte × (1 + log(écoutes + 1))
|
||||
Et "Balade Paris" est mieux classé grâce à sa popularité
|
||||
|
||||
Scénario: Autocomplete pendant la frappe
|
||||
Étant donné que l'utilisateur commence à taper "par"
|
||||
Quand 3 caractères sont saisis
|
||||
Alors des suggestions apparaissent:
|
||||
| suggestion |
|
||||
| paris |
|
||||
| parc naturel |
|
||||
| parvis notre-dame |
|
||||
Et le top 5 des suggestions est affiché
|
||||
|
||||
Scénario: Historique des 10 dernières recherches
|
||||
Étant donné que l'utilisateur a effectué les recherches suivantes:
|
||||
| recherche | date |
|
||||
| voyage paris | 2026-01-20 |
|
||||
| audio-guide louvre | 2026-01-19 |
|
||||
| podcast automobile | 2026-01-18 |
|
||||
Quand l'utilisateur ouvre la barre de recherche
|
||||
Alors les 10 dernières recherches sont affichées
|
||||
Et elles sont triées par date décroissante
|
||||
|
||||
Scénario: Correction automatique si aucun résultat
|
||||
Étant donné que l'utilisateur recherche "ballade paris" (faute d'orthographe)
|
||||
Et qu'aucun résultat n'est trouvé
|
||||
Quand la page de résultats s'affiche
|
||||
Alors une suggestion "Essayez plutôt : balade paris" est affichée
|
||||
|
||||
Scénario: Recherches populaires suggérées
|
||||
Étant donné qu'aucun résultat n'est trouvé pour une recherche
|
||||
Quand la page s'affiche
|
||||
Alors des suggestions populaires sont affichées:
|
||||
| suggestion |
|
||||
| balade paris |
|
||||
| audio-guide louvre |
|
||||
| visite montmartre |
|
||||
|
||||
# 15.3.2 - Recherche géographique
|
||||
|
||||
Scénario: Saisie d'un lieu avec autocomplete
|
||||
Étant donné que l'utilisateur ouvre le filtre "Lieu"
|
||||
Quand il tape "Louv"
|
||||
Alors Nominatim retourne des suggestions:
|
||||
| suggestion | type |
|
||||
| Musée du Louvre, Paris | monument |
|
||||
| Louvres, Val-d'Oise | commune |
|
||||
|
||||
Scénario: Sélection d'un lieu et définition du rayon
|
||||
Étant donné que l'utilisateur sélectionne "Paris, France"
|
||||
Et que les coordonnées sont (48.8566, 2.3522)
|
||||
Quand il définit un rayon de 50 km
|
||||
Alors la recherche PostGIS utilise ST_DWithin avec 50000 mètres
|
||||
|
||||
Plan du Scénario: Recherche géographique avec différents rayons
|
||||
Étant donné un contenu à 30 km de Paris
|
||||
Quand l'utilisateur recherche autour de Paris avec un rayon de <rayon>
|
||||
Alors le contenu est <résultat>
|
||||
|
||||
Exemples:
|
||||
| rayon | résultat |
|
||||
| 20 km | non trouvé |
|
||||
| 50 km | trouvé |
|
||||
| 100 km | trouvé |
|
||||
|
||||
Scénario: Utilisation de "Autour de moi" (GPS actuel)
|
||||
Étant donné que l'utilisateur active le GPS
|
||||
Et que sa position est (48.8566, 2.3522)
|
||||
Quand il sélectionne "Autour de moi"
|
||||
Alors la recherche utilise ses coordonnées GPS actuelles
|
||||
Et un rayon par défaut de 10 km est appliqué
|
||||
|
||||
Scénario: Curseur de rayon avec limites
|
||||
Étant donné que l'utilisateur ouvre le curseur de rayon
|
||||
Quand il ajuste le curseur
|
||||
Alors les valeurs disponibles vont de 5 km à 500 km
|
||||
Et la valeur s'affiche en temps réel "50 km"
|
||||
|
||||
Scénario: Affichage de la distance dans les résultats
|
||||
Étant donné une recherche géographique autour de Paris
|
||||
Et un contenu à 2.3 km de distance
|
||||
Quand les résultats sont affichés
|
||||
Alors la distance "À 2.3 km" est indiquée pour chaque résultat
|
||||
|
||||
Plan du Scénario: Tri par proximité géographique
|
||||
Étant donné des contenus à différentes distances de Paris:
|
||||
| contenu | distance |
|
||||
| Louvre Guide | 0.5 km |
|
||||
| Tour Eiffel | 2.0 km |
|
||||
| Versailles | 20 km |
|
||||
Quand l'utilisateur trie par "Proximité"
|
||||
Alors les résultats sont affichés dans l'ordre:
|
||||
| position | contenu |
|
||||
| 1 | Louvre Guide |
|
||||
| 2 | Tour Eiffel |
|
||||
| 3 | Versailles |
|
||||
|
||||
Scénario: Géocodage avec Nominatim (MVP)
|
||||
Étant donné que l'application est en phase MVP
|
||||
Quand une requête de géocodage est effectuée
|
||||
Alors l'API publique Nominatim est utilisée
|
||||
Et le rate limit de 1 req/s est respecté
|
||||
|
||||
Scénario: Géocodage avec fallback Mapbox
|
||||
Étant donné que Nominatim ne retourne aucun résultat
|
||||
Quand l'application tente un fallback
|
||||
Alors l'API Mapbox Geocoding est utilisée
|
||||
Et le coût de 0.50€ / 1000 requêtes est appliqué
|
||||
|
||||
# 15.3.3 - Filtres avancés
|
||||
|
||||
Scénario: Ouverture du panneau de filtres
|
||||
Étant donné que l'utilisateur est sur la page de recherche
|
||||
Quand il clique sur "Filtres"
|
||||
Alors un panneau latéral s'ouvre
|
||||
Et 7 catégories de filtres sont affichées:
|
||||
| catégorie |
|
||||
| Type de contenu |
|
||||
| Durée |
|
||||
| Classification âge |
|
||||
| Géo-pertinence |
|
||||
| Tags |
|
||||
| Date de publication |
|
||||
| Abonnement |
|
||||
|
||||
Scénario: Filtre par type de contenu (multi-sélection)
|
||||
Étant donné que l'utilisateur ouvre les filtres
|
||||
Quand il sélectionne:
|
||||
| type |
|
||||
| Contenu court |
|
||||
| Audio-guide |
|
||||
Alors seuls ces types de contenus sont recherchés
|
||||
Et les podcasts et radios live sont exclus
|
||||
|
||||
Plan du Scénario: Filtre par durée
|
||||
Étant donné un contenu de <durée> minutes
|
||||
Quand l'utilisateur filtre par "<tranche>"
|
||||
Alors le contenu est <résultat>
|
||||
|
||||
Exemples:
|
||||
| durée | tranche | résultat |
|
||||
| 3 | <5 min | trouvé |
|
||||
| 3 | 5-15 min | non trouvé |
|
||||
| 10 | 5-15 min | trouvé |
|
||||
| 20 | 15-30 min | trouvé |
|
||||
| 45 | >30 min | trouvé |
|
||||
|
||||
Scénario: Filtre par classification âge
|
||||
Étant donné des contenus avec différentes classifications:
|
||||
| contenu | classification |
|
||||
| Conte enfants | Tout public |
|
||||
| Podcast news | 13+ |
|
||||
| Débat politique | 16+ |
|
||||
Quand l'utilisateur filtre "Tout public"
|
||||
Alors seul "Conte enfants" est affiché
|
||||
|
||||
Scénario: Filtre par géo-pertinence
|
||||
Étant donné des contenus avec différents types géo:
|
||||
| contenu | type_geo |
|
||||
| Guide Louvre | Ancré |
|
||||
| Podcast Paris | Contextuel |
|
||||
| News nationales | Neutre |
|
||||
Quand l'utilisateur filtre "Ancré, Contextuel"
|
||||
Alors "Guide Louvre" et "Podcast Paris" sont affichés
|
||||
Et "News nationales" est exclu
|
||||
|
||||
Scénario: Filtre par tags (multi-sélection)
|
||||
Étant donné des contenus taggés:
|
||||
| contenu | tags |
|
||||
| Voyage en Italie | Voyage, Gastronomie |
|
||||
| Histoire de Rome | Voyage, Histoire |
|
||||
| Économie italienne | Économie |
|
||||
Quand l'utilisateur sélectionne les tags "Voyage, Histoire"
|
||||
Alors "Histoire de Rome" est en priorité (2 tags correspondants)
|
||||
Et "Voyage en Italie" est affiché (1 tag correspondant)
|
||||
Et "Économie italienne" est exclu
|
||||
|
||||
Plan du Scénario: Filtre par date de publication
|
||||
Étant donné un contenu publié il y a <délai>
|
||||
Quand l'utilisateur filtre par "<période>"
|
||||
Alors le contenu est <résultat>
|
||||
|
||||
Exemples:
|
||||
| délai | période | résultat |
|
||||
| 12 heures | Dernières 24h | trouvé |
|
||||
| 3 jours | Cette semaine | trouvé |
|
||||
| 15 jours | Ce mois | trouvé |
|
||||
| 8 mois | Cette année | trouvé |
|
||||
| 2 ans | Toutes dates | trouvé |
|
||||
| 2 ans | Cette année | non trouvé |
|
||||
|
||||
Scénario: Filtre par type d'abonnement
|
||||
Étant donné des contenus gratuits et Premium:
|
||||
| contenu | type |
|
||||
| Balade Paris | Gratuit |
|
||||
| Visite VIP Louvre | Premium |
|
||||
Quand l'utilisateur filtre "Premium uniquement 👑"
|
||||
Alors seul "Visite VIP Louvre" est affiché
|
||||
|
||||
Scénario: Combinaison de filtres multiples (AND logic)
|
||||
Étant donné que l'utilisateur applique les filtres:
|
||||
| filtre | valeur |
|
||||
| Type | Audio-guide |
|
||||
| Durée | 5-15 min |
|
||||
| Tags | Voyage |
|
||||
| Classification | Tout public |
|
||||
Quand la recherche est lancée
|
||||
Alors seuls les contenus respectant TOUS les critères sont affichés
|
||||
|
||||
Scénario: Réinitialisation des filtres
|
||||
Étant donné que l'utilisateur a appliqué 5 filtres différents
|
||||
Quand il clique sur "Réinitialiser"
|
||||
Alors tous les filtres sont désactivés
|
||||
Et la recherche affiche tous les résultats
|
||||
|
||||
Scénario: Sauvegarde d'une recherche
|
||||
Étant donné que l'utilisateur a appliqué plusieurs filtres
|
||||
Quand il clique sur "💾 Sauvegarder cette recherche"
|
||||
Et qu'il entre le nom "Podcasts voyage Paris"
|
||||
Alors la recherche est sauvegardée
|
||||
Et elle apparaît dans l'onglet "Recherches sauvegardées"
|
||||
|
||||
Scénario: Limite de 5 recherches sauvegardées
|
||||
Étant donné que l'utilisateur a déjà 5 recherches sauvegardées
|
||||
Quand il tente de sauvegarder une 6ème recherche
|
||||
Alors un message d'erreur s'affiche
|
||||
Et il doit supprimer une recherche existante avant d'en ajouter une nouvelle
|
||||
|
||||
Scénario: Notifications pour recherches sauvegardées
|
||||
Étant donné une recherche sauvegardée "Podcasts voyage Paris"
|
||||
Et que l'utilisateur a activé les notifications
|
||||
Quand 3 nouveaux contenus correspondants sont publiés
|
||||
Alors une notification "3 nouveaux contenus dans 'Podcasts voyage Paris'" est envoyée
|
||||
|
||||
Plan du Scénario: Options de tri des résultats
|
||||
Étant donné une recherche avec plusieurs résultats
|
||||
Quand l'utilisateur sélectionne le tri "<option>"
|
||||
Alors les résultats sont triés selon <algorithme>
|
||||
|
||||
Exemples:
|
||||
| option | algorithme |
|
||||
| Pertinence | Score recherche × (1 + log(écoutes + 1)) |
|
||||
| Popularité | Écoutes complètes derniers 30j DESC |
|
||||
| Récent | Date publication DESC |
|
||||
| Proximité | Distance GPS ASC (si recherche géo) |
|
||||
| Durée | Durée audio ASC ou DESC |
|
||||
|
||||
# 15.3.4 - Page de résultats
|
||||
|
||||
Scénario: Structure d'un résultat de recherche
|
||||
Étant donné un résultat de recherche
|
||||
Quand la page est affichée
|
||||
Alors chaque résultat contient:
|
||||
| élément | exemple |
|
||||
| Cover image | 120×68 px (16:9) |
|
||||
| Titre | Balade à Paris (2 lignes max) |
|
||||
| Créateur | @paris_stories ✓ |
|
||||
| Durée | 12 min |
|
||||
| Écoutes | 🎧 2.3K |
|
||||
| Localisation | 📍 Paris 5e · Ancré |
|
||||
| Tags | 🏷️ #Voyage #Histoire |
|
||||
| Badge Premium | 👑 (si applicable) |
|
||||
| Distance | À 2.3 km (si recherche géo) |
|
||||
| Bouton lecture | ▶️ Écouter |
|
||||
| Menu contextuel | ⋮ |
|
||||
|
||||
Scénario: Lazy loading des images
|
||||
Étant donné une page avec 20 résultats de recherche
|
||||
Quand la page se charge
|
||||
Alors seules les 5 premières images sont chargées
|
||||
Et les images suivantes se chargent au scroll
|
||||
|
||||
Scénario: Troncature du titre sur 2 lignes maximum
|
||||
Étant donné un contenu avec un titre de 120 caractères
|
||||
Quand le résultat est affiché
|
||||
Alors le titre est tronqué après 2 lignes
|
||||
Et "..." est ajouté à la fin
|
||||
|
||||
Scénario: Lien cliquable vers le profil créateur
|
||||
Étant donné un résultat de recherche pour "@paris_stories"
|
||||
Quand l'utilisateur clique sur "@paris_stories"
|
||||
Alors il est redirigé vers "https://roadwave.fr/@paris_stories"
|
||||
|
||||
Scénario: Menu contextuel d'un résultat [⋮]
|
||||
Étant donné que l'utilisateur clique sur [⋮] pour un résultat
|
||||
Quand le menu s'ouvre
|
||||
Alors les actions suivantes sont disponibles:
|
||||
| action |
|
||||
| Partager |
|
||||
| Ajouter à une playlist |
|
||||
| Télécharger (offline) |
|
||||
| Signaler |
|
||||
|
||||
Scénario: Pagination avec 20 résultats par page
|
||||
Étant donné une recherche retournant 100 résultats
|
||||
Quand la page est affichée
|
||||
Alors 20 résultats sont chargés initialement
|
||||
Et un indicateur "1-20 sur 100 résultats" est visible
|
||||
|
||||
Scénario: Infinite scroll automatique
|
||||
Étant donné que l'utilisateur scroll dans les résultats
|
||||
Quand il atteint 80% de la page
|
||||
Alors les 20 résultats suivants sont chargés automatiquement
|
||||
Et un loader est affiché pendant le chargement
|
||||
|
||||
Scénario: Bouton fallback "Charger 20 suivants"
|
||||
Étant donné que l'infinite scroll est désactivé (paramètres)
|
||||
Quand l'utilisateur atteint la fin de la page
|
||||
Alors un bouton "Charger 20 suivants" est affiché
|
||||
Et les résultats se chargent au clic
|
||||
|
||||
# Vue carte
|
||||
|
||||
Scénario: Basculement entre vue liste et vue carte
|
||||
Étant donné que l'utilisateur est sur la page de résultats
|
||||
Quand il clique sur le toggle "Liste / Carte"
|
||||
Alors la vue carte Leaflet s'affiche
|
||||
Et les résultats sont affichés comme markers sur la carte
|
||||
|
||||
Scénario: Affichage de la carte Leaflet
|
||||
Étant donné que la vue carte est activée
|
||||
Quand la carte se charge
|
||||
Alors la carte utilise les tuiles OpenStreetMap
|
||||
Et le centre est la position de recherche (ou GPS utilisateur)
|
||||
Et le zoom initial montre tous les résultats
|
||||
|
||||
Scénario: Markers cliquables sur la carte
|
||||
Étant donné que 10 résultats sont affichés sur la carte
|
||||
Quand l'utilisateur clique sur un marker
|
||||
Alors une popup s'affiche avec:
|
||||
| élément |
|
||||
| Titre |
|
||||
| Créateur |
|
||||
| Durée |
|
||||
| Distance |
|
||||
| Bouton ▶️ Écouter|
|
||||
|
||||
Scénario: Clustering des markers proches
|
||||
Étant donné que 50 résultats sont très proches géographiquement
|
||||
Quand la carte est affichée
|
||||
Alors les markers proches sont groupés en clusters
|
||||
Et le nombre de contenus est affiché sur le cluster
|
||||
Et le cluster se décompose au zoom
|
||||
|
||||
Scénario: Synchronisation liste / carte
|
||||
Étant donné que l'utilisateur est en vue carte
|
||||
Quand il clique sur un marker et écoute le contenu
|
||||
Et qu'il rebascule en vue liste
|
||||
Alors le contenu écouté est marqué dans la liste
|
||||
Et la position de scroll est maintenue
|
||||
|
||||
# Performances et index
|
||||
|
||||
Scénario: Index PostgreSQL full-text pour performances
|
||||
Étant donné que la base contient 100K contenus
|
||||
Quand une recherche full-text est effectuée
|
||||
Alors l'index GIN sur to_tsvector est utilisé
|
||||
Et la requête retourne en moins de 100ms
|
||||
|
||||
Scénario: Index PostGIS GIST pour recherche géo
|
||||
Étant donné une recherche géographique avec rayon 50 km
|
||||
Quand la requête PostGIS ST_DWithin est exécutée
|
||||
Alors l'index GIST sur la colonne location est utilisé
|
||||
Et la requête retourne en moins de 50ms
|
||||
|
||||
Scénario: Index composites pour filtres
|
||||
Étant donné une recherche avec filtres multiples
|
||||
Quand les filtres type, durée, âge, géo, date sont appliqués
|
||||
Alors l'index composite idx_content_filters est utilisé
|
||||
Et les performances restent optimales
|
||||
|
||||
Scénario: Index GIN pour recherche par tags
|
||||
Étant donné une recherche filtrée par tags "Voyage, Histoire"
|
||||
Quand la requête est exécutée
|
||||
Alors l'index GIN sur la colonne tags est utilisé
|
||||
Et la recherche est performante même avec 500K contenus
|
||||
|
||||
# Cas d'erreur
|
||||
|
||||
Scénario: Aucun résultat trouvé
|
||||
Étant donné que l'utilisateur recherche "xyzabc123"
|
||||
Quand aucun résultat n'est trouvé
|
||||
Alors un message "Aucun résultat pour 'xyzabc123'" s'affiche
|
||||
Et des suggestions de recherches populaires sont proposées
|
||||
|
||||
Scénario: Recherche vide
|
||||
Étant donné que l'utilisateur clique sur "Rechercher" sans saisir de texte
|
||||
Quand la recherche est lancée
|
||||
Alors un message "Veuillez entrer au moins 2 caractères" s'affiche
|
||||
|
||||
Scénario: Erreur de géocodage Nominatim
|
||||
Étant donné que l'API Nominatim est indisponible
|
||||
Quand l'utilisateur tente une recherche géographique
|
||||
Alors un message "Service de localisation temporairement indisponible" s'affiche
|
||||
Et la recherche continue sans filtre géographique
|
||||
|
||||
Scénario: GPS désactivé pour "Autour de moi"
|
||||
Étant donné que l'utilisateur a désactivé le GPS
|
||||
Quand il sélectionne "Autour de moi"
|
||||
Alors un message "Veuillez activer la localisation" s'affiche
|
||||
Et un bouton "Activer" ouvre les paramètres système
|
||||
|
||||
Scénario: Timeout de recherche après 10 secondes
|
||||
Étant donné qu'une recherche complexe est lancée
|
||||
Quand la requête dépasse 10 secondes
|
||||
Alors la recherche est annulée
|
||||
Et un message "La recherche a pris trop de temps, veuillez réessayer" s'affiche
|
||||
Reference in New Issue
Block a user