07.01 — Arborescence#
src/
├── main.py # entry point (cf. README)
│
├── api/ # clients HTTP externes
│ ├── retrieve_dashboard_otp_code.py # filter + sort + pick OTP
│ ├── get_otp_history.py # HTTP GET /api/otp-history
│ └── constants/endpoints.py # URLs des endpoints tests-workers
│
├── caches/ # cache L1 in-memory
│ ├── l1.py # dogpile.cache.memory TTL 30m
│ └── reserve_free_cache_key.py # UUID + threading.Lock
│
├── constants/ # constantes
│ ├── pages/
│ │ ├── homepage.py # HOMEPAGE_URL
│ │ ├── dashboard.py # DASHBOARD_URL + ids des inputs
│ │ ├── random_loaders.py
│ │ ├── sacred_upload.py
│ │ ├── corsicamon.py
│ │ ├── chaotic_form.py
│ │ ├── madness.py
│ │ ├── random_error_page.py
│ │ └── donkey_sausage_eater_detector.py
│ └── sys/
│ ├── redis_keys.py # OTP_SEND_LOCK_KEY, ...
│ └── transient_errors.py # (WebDriverException, HttpError...)
│
├── lib/
│ ├── connectors/test_steps/actions/ # fonctions (TPOM) -> TPOM
│ │ ├── homepage.py
│ │ ├── dashboard_login.py # ~10 connectors (with/without retries)
│ │ ├── dashboard_welcome.py
│ │ ├── dashboard_protected_page.py
│ │ ├── sacred_upload.py
│ │ ├── corsicamon_enter_api_key.py
│ │ ├── corsicamon_main.py
│ │ ├── chaotic_form.py
│ │ ├── random_error.py
│ │ ├── random_loaders.py
│ │ ├── madness.py
│ │ ├── this_is_bastia.py
│ │ ├── cors_errors.py
│ │ ├── bsod.py
│ │ ├── dsed.py
│ │ └── ids_bypassed.py
│ ├── custom_errors/
│ │ ├── http.py # HttpErrorPageReachedError
│ │ └── transient_error.py # TransientError
│ └── ext/ # extensions / adapters
│ ├── ocarina/
│ │ ├── adapters/
│ │ │ ├── agnostic/
│ │ │ │ ├── act.py # ⭐ adapter act (hook on_failure)
│ │ │ │ ├── env_getters.py # ⭐ adapter EnvGetters typé
│ │ │ │ └── match_page.py # ⭐ adapter match_page
│ │ │ └── selenium/
│ │ │ ├── test_suite.py # ⭐ adapter TestSuite
│ │ │ ├── test_campaign.py # ⭐ adapter TestCampaign
│ │ │ ├── cli_getters.py # getters typés depuis le CliStore
│ │ │ ├── logs.py # create_just_log_error, etc.
│ │ │ └── screenshotter.py # take_screenshot helper
│ │ └── regex/error_page.py # ERROR_PAGE_REGEX
│ ├── redis/
│ │ └── client.py # Singleton client Redis
│ └── selenium/
│ ├── humanize/ # ⭐ HumanizedDriver + keyboard
│ │ ├── proxy.py
│ │ └── keyboard.py
│ ├── pages/verify_elements_presence.py
│ └── watchers/catch_me_if_you_can_watcher.py # ⭐ Watcher callback
│
├── pages/ # POMs (Selenium + SeleniumTitleMixin)
│ ├── homepage.py
│ ├── random_loaders.py
│ ├── chaotic_form.py
│ ├── random_error.py
│ ├── sacred_upload/
│ │ ├── sacred_upload.py
│ │ └── fixtures/ # fichiers à uploader
│ ├── dashboard/
│ │ ├── login.py # ⭐ avec OTP, retries, redis locks
│ │ ├── welcome_page.py
│ │ └── protected_page.py
│ ├── corsicamon/
│ │ ├── enter_api_key.py
│ │ └── main.py
│ ├── madness/
│ │ ├── base.py
│ │ ├── matchers.py # has_cors, has_this_is_bastia
│ │ ├── cors.py
│ │ └── this_is_bastia.py
│ └── donkey_sausage_detector/
│ └── ids_bypassed.py
│
└── tests/
├── cycles/e2e.py # ⭐ TestCycle (smoke + main)
├── campaigns/
│ ├── global_smoke_tests.py # smoke
│ ├── corsicamon.py # main (+ smoke variant)
│ ├── dashboard_login.py # main
│ ├── randomness.py # main
│ └── sacred_upload.py # main
├── suites/
│ ├── global_smoke_tests.py
│ ├── randomness.py
│ ├── dashboard/
│ │ ├── access/
│ │ │ ├── happy_paths.py
│ │ │ └── unhappy_paths.py
│ │ └── data_driven/multi_login.py
│ ├── sacred_upload/{happy_paths,unhappy_paths}.py
│ └── corsicamon/{smoke_tests,happy_paths,unhappy_paths}.py
└── scenarios/
├── homepage/verify_homepage.py
├── dashboard/
│ ├── access/{happy_paths,unhappy_paths}.py
│ ├── back_to_igoristan.py
│ └── data_driven/{multi_login,datasets/multi_login}.py
├── sacred_upload/{upload_files,just_go_back_to_igoristan}.py
├── corsicamon/{enter_api_key,new_draw,add_corsicamon,back_to_igoristan}.py
└── randomness/
├── level_1/{random_error_page,random_loaders_page}.py
├── level_2/{dsed,madness}.py
├── level_3/chaotic_form.py
└── level_4/walkthrough.pySémantique des dossiers#
| Dossier | Rôle | Convention |
|---|---|---|
pages/ | POMs (héritent SeleniumTitleMixin, POMBase) | Une classe par page, fluent (return self) |
lib/connectors/test_steps/actions/ | Connectors (TPOM) -> TPOM | Fonctions pures ; si paramètres, closures |
lib/custom_errors/ | Exceptions projet | Sous-classes d’Exception |
lib/ext/ocarina/adapters/ | Adapters au-dessus d’Ocarina | Cf. 02-adapters.md |
lib/ext/redis/ | Wrapper Singleton Redis | Pour les locks distribués |
lib/ext/selenium/ | Extensions Selenium spécifiques au projet | HumanizedDriver, Watcher callback |
caches/ | Cache L1 in-memory | dogpile.cache.memory + UUID reservation |
constants/ | Constantes | URLs, redis keys, transient errors |
api/ | Clients HTTP externes | OTP retrieval, OTP history |
tests/ | Hiérarchie ISTQB | cycles / campaigns / suites / scenarios |
lib/ vs pages/#
pages/: POMs, encapsulent l’état d’une page (locators, méthodes d’action).lib/connectors/test_steps/actions/: connectors, fonctions qui réfèrent aux POMs.
act(on_homepage, open_homepage)
# ^^^^^^^^^^^^^
# connector défini dans lib/connectors/test_steps/actions/homepage.pydef open_homepage(p: Homepage) -> Homepage:
return p.open()Le CLAUDE.md du projet IA en fait une discipline :