8e6ee31596
ci / test (push) Successful in 6m18s
History-free snapshot of the private development repository at cbe45c5 (2026-09-09). Code, docs and public catalogue only; no plant or job knowledge layers, no client or job names (tools/scrub.py clean). The intro deck is left out of this snapshot because its slide images could not be checked for job drawings.
78 lines
3.2 KiB
Python
78 lines
3.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Host-side guard for issue #23's `live` kv contract.
|
|
|
|
The issue (owner, 2026-09-07: "啥叫JS活儿?html写不了?" -- the front end is
|
|
ours, so write it): a kv row that sums a row table -- the Reinforcement
|
|
page's Strand count, Total IP and Aps -- must follow the table's rows IN
|
|
THE BROWSER as they are edited, not only on the next page build / Run.
|
|
The page is plain ES2017 in a static file with no JS test runner, so this
|
|
suite checks the CONTRACT by reading the source, the same way
|
|
`test_button_tooltips.py` reads `bundle.yaml` / `script.py`: the three
|
|
pieces that wire the feature together must all exist and stay connected --
|
|
`renderKv` records the spec into `state.liveKv`, `recomputeLiveKv` rewrites
|
|
the value cells, and `updateValue` (plus the row add/remove handlers) calls
|
|
it whenever a key of the table changes. And the spec document must still
|
|
describe the row shape (docs/pa-web-spec.md, "kv").
|
|
"""
|
|
import io
|
|
import os
|
|
import re
|
|
|
|
_EXT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
_REPO = os.path.dirname(_EXT)
|
|
|
|
APP_JS = os.path.join(_REPO, 'tools', 'pa_web', 'static', 'app.js')
|
|
SPEC_MD = os.path.join(_REPO, 'docs', 'pa-web-spec.md')
|
|
|
|
|
|
def _read(path):
|
|
with io.open(path, encoding='utf-8') as f:
|
|
return f.read()
|
|
|
|
|
|
def test_the_files_this_guards_still_exist():
|
|
"""If the page moves, this suite must fail loudly rather than pass by
|
|
reading nothing -- the test_button_tooltips.py discipline."""
|
|
assert os.path.isfile(APP_JS), 'missing {0}'.format(APP_JS)
|
|
assert os.path.isfile(SPEC_MD), 'missing {0}'.format(SPEC_MD)
|
|
|
|
|
|
def test_app_js_declares_the_live_kv_mechanism():
|
|
src = _read(APP_JS)
|
|
# renderKv records rows into state.liveKv, keyed by the spec's table...
|
|
assert 'state.liveKv' in src
|
|
assert 'function recomputeLiveKv' in src
|
|
# ...the value formatting helper exists, and row discovery is the SAME
|
|
# helper the editable table itself uses (one idea of "present row").
|
|
assert 'function fmtLive' in src
|
|
assert 'rowIds(live.table)' in src
|
|
|
|
|
|
def test_update_value_recomputes_live_kv_rows():
|
|
"""`updateValue` -- every cell edit's one funnel -- must call
|
|
`recomputeLiveKv`, not just `recomputeDerived`. Sliced to updateValue's
|
|
own body so a stray later call elsewhere cannot satisfy the guard."""
|
|
src = _read(APP_JS)
|
|
start = src.index('function updateValue')
|
|
end = src.find('\n\n // ---', start)
|
|
assert end != -1, 'could not find the end of updateValue\'s body'
|
|
body = src[start:end]
|
|
assert 'recomputeLiveKv(' in body, \
|
|
'updateValue no longer calls recomputeLiveKv after recomputeDerived'
|
|
|
|
|
|
def test_spec_documents_live_under_kv():
|
|
md = _read(SPEC_MD)
|
|
start = md.index('### `kv`')
|
|
nxt = md.find('\n### ', start + 1)
|
|
assert nxt != -1, 'no section follows kv in pa-web-spec.md'
|
|
section = md[start:nxt]
|
|
assert re.search(r'\[label, value, live\]', section)
|
|
assert '"live"' in section
|
|
assert '"terms"' in section and '"format"' in section
|
|
assert '"unknown"' in section
|
|
# the contract names the trigger: any key of the table, add/remove row
|
|
# included, and JS-off correctness (server value is the first paint).
|
|
assert '.present' in section
|
|
assert 'JS off' in section
|