Dinoer

Writing a scenario

A JSON file, a list of actions, eighteen verbs. How to write one from a page you have read, and the two habits that keep it working.

A scenario is a JSON file: where to go, and what to do there. It is the unit of work of the browser core.

{
  "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. The file is checked against scenarios/schema.json before the browser starts: a misspelt key stops the run at once, not halfway through.

Start by reading the page

You cannot write actions for a page you have not read. Read 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; the reconnaissance scenario gives you the selectors. Write the scenario from that output, not from what the page probably looks like. The reconnaissance scenario →

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
Second factorattendre_mfa_ntfy
Composedeclencher_scenario, nettoyer_overlay

An action that targets an element takes a CSS selector; there is no numbered overlay to use instead. Why →

The required and optional keys of each verb, 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, and it can be lost either way.

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

With a ten-second pause, an operation that takes fifteen is read while it is still running: success reported, nothing verified. One that takes two wastes eight seconds on every run. Waiting for a change in the page makes the scenario last exactly as long as the work.

Habit two: check the page before acting

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

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

This check is what stands between a redirect you did not notice and a password typed into someone else’s form. auth_indicator names a selector that only exists once signed in; the answer then reports whether it was found (auth_status):

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

Options the file can carry

Some settings belong to the target, not to whoever runs the scenario. Put them in the file:

PropertyFor
wait_untila target whose network never goes quiet
http_credentialsHTTP Basic authentication
auth_indicatorchecking that the session is signed in
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. That is what lets you commit a scenario. Dinoer refuses to run a scenario that contains a secret written in clear, in a remplir on a field that looks sensitive or inside an evaluer script.

The encrypted credential directory →

In short

  • Read the page first with --a11y; write the scenario from that output.
  • url and actions are required; everything else is optional.
  • An element is always targeted by a CSS selector.
  • Wait for signals, not durations.
  • Check the page before acting on it.
  • A credential is always a secret_cle, never a value, anywhere in the file.