The scripts lived in the browser's localStorage although only the local
server can run them. The new test_scripts.py store
(~/.pysim-simple-server/test_scripts.json, --test-scripts PATH) sits next to
the card presets: stable uuids and timestamps, atomic writes, and every
mutation is validated with the runner's own engine (the submitted shape is
kept - the normaliser's output is not re-normalisable: it fills defaults
like an 'any' PoR check that the check normaliser then rejects), so a stored
script cannot fail at run start for a validation reason. Import accepts the
old localStorage export as-is (export/import remains the migration path; the
PWA import also takes an exported array).
- /api/test/scripts (GET card-free: path/count + scripts), POST create,
/update, /delete and /import serve the store; the endpoints answer 503
without a store and 500 on a write failure, like the preset endpoints.
- /api/test/run takes a stored script_id (resolved when no inline script is
sent); the PWA sends the edited copy inline plus the id for the report,
and the run snapshot carries script_id.
- The PWA's Test script pill loads from the store, writes through on edits
(debounced name, immediate step/new/copy/delete), reports load/save/delete
failures inline, and exports the portable {name, steps} shape.
Tests: 808 frontend + 701 Python; sw cache simple-v379.
Three gaps blocked SCP80 applet testing from the PWA:
- testRunPreset() still marshalled the pre-v3.8 flat preset fields
(kic/kid/keys/counter/tar/spi1/spi2, all empty on a stored preset), so
every SCP80 run failed with "SCP80 preset is incomplete: no keyset is
defined". The PWA sends the matched preset's id now and the server
resolves it from the store (_test_run_preset_from_body; the inline
`preset` body stays for API callers), so a run always uses the stored
keysets, TARs and counters. Regression tests on both sides.
- testscript._normalise_params dropped the scp80 step's `kvn` (the editor
exposed it and the runner and validation consumed it, so scripts
silently used the preset's first keyset). kvn is validated (1-15) and
passed through; the client-side problem check bounds it too.
- The PoR delivered as a SEND SHORT MESSAGE (PoR-in-submit, SPI2 bit 0x20)
is decoded for the expect step that fetches it: the new `por` content
check asserts the response status / R-APDU SW / response data, decoded
with the preceding scp80 step's context (concatenated SMS accumulate
across expectations). The inline transport stays on the action
(check.por / check.data).
- The new `files` content check asserts the File List of a REFRESH
(TS 102 223 8.18, tag 12/92) - e.g. qualifier 00 = NAA initialization
with full file change notification - as an order-insensitive set of FID
paths.
The step editor exposes both checks; help EN/RU, docs/api.md and the new
authoring reference docs/test-scripts.md document the format. Tests: 803
frontend + 680 Python; sw cache simple-v378.