Dinoer

Writing a scenario

A JSON file, a list of actions, eighteen verbs — none of them numbered. How to build one that still works next month, and the two habits that decide whether it does.

A scenario is a JSON file: where to go, and what to do there. It is the retained browser core’s unit of work — you will write them, reuse them, and put them under version control.

{
  "nom": "check-dashboard",
  "url": "https://target.local/login",
  "actions": [
    {"type": "remplir", "selecteur": "input[name=\"username\"]", "valeur": "depuis_secrets", "secret_cle": "username"},
    {"type": "remplir", "selecteur": "input[name=\"password\"]", "valeur": "depuis_secrets", "secret_cle": "password"},
    {"type": "cliquer", "selecteur": "button[type=\"submit\"]"},
    {"type": "attendre_selecteur_present", "selecteur": ".user-menu"}
  ]
}

Two fields are required: url and actions. Everything else is optional, and the file is validated against scenarios/schema.json before a browser starts — a typo in a key name stops the run immediately rather than halfway through.

Start by looking, without mutating

You cannot write actions for a page you have not read. Map it first:

/opt/dinoer/venv/bin/python3 /opt/dinoer/shot.py \
  --url https://target.local/login --a11y --guide-version 1.6

a11y_tree gives you the structure and the selectors. Write the scenario from that output, not from memory of what the page probably looks like — the guide’s own reconnaissance-before-mutation rule, not a suggestion. The reconnaissance scenario, run for real →

The eighteen verbs

FamilyActions
Actcliquer, remplir, defiler
Inside a framecliquer_iframe, remplir_iframe
Movenaviguer
Waitattendre, attendre_absence, attendre_selecteur_present, attendre_navigation, attendre_url, attendre_reseau_calme, pause
Observeevaluer, extraire_texte
MFAattendre_mfa_ntfy
Composedeclencher_scenario, nettoyer_overlay

Every action targets a CSS selector — there is no numbered overlay to target instead, on purpose. Why →

Each verb with its required and optional keys, and the exit codes, are on the cheat sheet.

Habit one — wait for a signal, never for a duration

A pause is a bet on how long something takes. You will lose it, in both directions.

{"type": "cliquer", "selecteur": "button[type=\"submit\"]"},
{"type": "attendre_absence", "selecteur": ".spinner"},
{"type": "attendre_selecteur_present", "selecteur": ".result"}

Set a pause to ten seconds and an operation taking fifteen gives you an answer read while the job is still running — success reported, nothing verified. One taking two wastes eight seconds on every single run. Waiting for a DOM signal makes the scenario take exactly as long as the work does.

Habit two — assert before you act

Put a check at the top. If the page is not the one you expect, the scenario stops before typing anything anywhere:

{"type": "evaluer", "script": "document.title", "contient": "Sign in"}

This costs nothing and it is what stands between a redirect you did not notice and a password typed into a stranger’s form. auth_indicator does the same at the file level — a selector that only exists when authenticated, checked automatically:

{"auth_indicator": ".user-menu", "url": "…", "actions": [...]}

Options the file can carry itself

Some flags belong to the target, not to whoever runs the scenario. Put them in the file so it stays self-contained:

PropertyFor
wait_untila target that never goes network-idle
http_credentialsHTTP Basic authentication
auth_indicatoran automatic authentication check
intentiona sentence recorded in the operations journal

Whoever reuses your scenario then does not need to know the target’s quirks.

Never write a credential

{"type": "remplir", "selecteur": "input[name=\"password\"]",
 "valeur": "depuis_secrets", "secret_cle": "password"}

The scenario names a key; the encrypted directory holds the value. This is what makes a scenario safe to commit, and the publication check refuses any run that finds a password in clear text in one — in a remplir field or inside an evaluer script.

The encrypted credential directory →

In short

  • Map the page first with --a11y; write the scenario from that output.
  • url and actions are required — everything else is optional.
  • Every target is a CSS selector — there is no other way to name one.
  • Wait for signals, not durations.
  • Assert the page before acting on it.
  • A credential is always a secret_cle, never a value, anywhere in the file.