| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- """The T8 fleet bundle (goal 41 task 8): the fleet + admin pages on the
- Spry conversion — the saddle cards' "now" head / "Keys & admin" tail
- split with the ONE consolidated health verdict line (saddle.health_verdict),
- the fleet health summary line above the grid when any saddle is
- offline/stale (saddles.fleet_alert), the capability catalogue as its own
- h2 "Routing tags" section, the register/capability create forms as
- native <dialog> modals behind buttons (the shared openAppDialog helper),
- the users page's create dialog + dense row actions, the model page's
- Spry form cards and the stack detail's in-place Details editor with the
- demoted delete. Markup-level via http_get_page; the slot-level verdict
- and alert cross-checked against the saddles slot data."""
- from harness import * # noqa: F401,F403 — the shared wire helpers
- def run(ctx):
- auth = ctx.auth
- print("== /saddles: the head/tail split markup ==")
- page = http_get_page(auth, "/saddles")
- # The admin tail folds under ONE expander after the verdict/work
- # head (ux-notes quick win 8 A); the fold carries the hairline.
- check("Keys & admin</summary>" in page,
- "the card tail folds under one Keys & admin expander")
- check(page.index("Keys & admin</summary>")
- > page.index("stm-text=\"saddle.health_verdict\""),
- "the admin tail sits after the verdict line (head first)")
- check("saddle-fold" in page,
- "the head/tail boundary carries the saddle-fold hairline")
- check("saddle.health_verdict" in page,
- "the card head renders the composed health verdict line")
- # The three old fragments no longer lead the head: the badge keeps
- # the colour, the verdict carries the words.
- check("'daemon ' + saddle.daemon_state" not in page
- and "'contacted ' + saddle.last_contact_display" not in page,
- "the fragmented health line is gone (one verdict instead)")
- check("stm-text=\"saddle.current_goal_title\"" in page
- and "stm-text=\"saddle.current_task_title\"" in page,
- "current work stays in the head")
- print("== /saddles: the fleet summary line + Routing tags section ==")
- # fleet_alert is server-composed; the page renders it when
- # non-empty. Mark a saddle offline to shape it, then restore.
- conn = db()
- conn.execute("update saddles set status = 'offline' where id = ?",
- (ctx.fake_id,))
- conn.commit()
- conn.close()
- state = slot_public(auth, "/saddles", "saddles")
- check(state["fleet_alert"].startswith("1 of ")
- and "not healthy" in state["fleet_alert"]
- and "fake-saddle" in state["fleet_alert"],
- "an offline saddle shapes the fleet alert line: %s"
- % state["fleet_alert"])
- check("dispatcher may be starved" in state["fleet_alert"],
- "the alert names the dispatcher risk")
- card = saddle_card(auth, "fake-saddle")
- check(card["health"] == "offline"
- and "offline" in card["health_verdict"],
- "the offline row's verdict agrees with its health")
- conn = db()
- conn.execute("update saddles set status = 'idle' where id = ?",
- (ctx.fake_id,))
- conn.commit()
- conn.close()
- state = slot_public(auth, "/saddles", "saddles")
- check(state["fleet_alert"] == "",
- "a healthy fleet renders no alert (empty string)")
- # The catalogue is its own h2 section (recommendation C), not an
- # h3 tail after the grid.
- check("<h2 id=\"routing-tags\">Routing tags</h2>" in page,
- "the capability catalogue is its own h2 Routing tags section")
- check("<h3>Capabilities</h3>" not in page,
- "the old h3 Capabilities tail is gone")
- print("== /saddles: the register + capability dialogs ==")
- check('id="saddle-register-dialog"' in page
- and "<dialog" in page,
- "the register form is a native dialog")
- check('onclick="window.openAppDialog(\'saddle-register-dialog\')"' in page,
- "the + Register saddle button opens the dialog via the helper")
- register_html = page[page.index('id="saddle-register-dialog"'):]
- check('stm-action="saddles.register"' in register_html
- and 'name="name"' in register_html
- and 'name="host"' in register_html,
- "the dialog carries the unchanged register form")
- check('class="dialog-titlebar"' in register_html
- and 'class="dialog-footer"' in register_html,
- "the register dialog uses Spry's dialog classes")
- check('id="capability-create-dialog"' in page
- and 'stm-action="saddles.create_capability"' in page,
- "the capability create form lives in a dialog too")
- check("<details>" in page and "<summary><strong>Register saddle</strong></summary>" not in page,
- "no create expander remains on the saddles page (details are the admin tail)")
- # Semantics: registering through the dialog form's action works,
- # then clean up (the fake saddle stays the fleet's only daemon).
- before = len(slot_public(auth, "/saddles", "saddles")["saddles"])
- act(auth, state["register"], {"name": "t8-probe", "host": "localhost:1"})
- rows = slot_public(auth, "/saddles", "saddles")["saddles"]
- check(len(rows) == before + 1, "the register action still creates")
- probe = [s for s in rows if s["name"] == "t8-probe"][0]
- # register_saddle creates the row IDLE (the proven-derivation
- # decision): a never-contacted saddle honestly reads unknown, and
- # the verdict says so without the alarm of "offline".
- check(probe["health"] == "unknown"
- and "no health report yet" in probe["health_verdict"],
- "a never-contacted saddle reads unknown with the no-report verdict")
- act(auth, probe["unregister"], {})
- check(len(slot_public(auth, "/saddles", "saddles")["saddles"]) == before,
- "the probe saddle unregisters again")
- print("== /users: create dialog + dense row actions ==")
- users_page = http_get_page(auth, "/users")
- check('id="user-create-dialog"' in users_page
- and 'stm-action="users.create"' in users_page
- and 'onclick="window.openAppDialog(\'user-create-dialog\')"' in users_page,
- "the user create form lives in a dialog behind the button")
- check("<summary><strong>New user</strong></summary>" not in users_page,
- "no create expander remains on the users page")
- check('name="confirm_password"' in users_page
- and 'name="date_of_birth"' in users_page,
- "the dialog carries the unchanged create fields")
- check("btn-danger" in users_page and "Delete this user?" in users_page,
- "the destructive delete is the visually secondary danger button")
- print("== /models: Spry form cards ==")
- models_page = http_get_page(auth, "/models")
- check('stm-action="model.save"' in models_page
- and 'id="model-base-url"' in models_page
- and 'id="advisor-base-url"' in models_page,
- "the model form keeps its fields with labelled controls")
- check('class="card-header"' in models_page and "form-rows" in models_page,
- "the endpoint groups are cards with capped-width fields")
- check("alert-warning" in models_page or "alert-info" in models_page,
- "the readiness line renders as an alert")
- # The saved hint round-trip already covered by modelconfig; the
- # save action stays the same one.
- print("== /stacks/{id}: in-place Details editor + demoted delete ==")
- stacks_state = slot_public(auth, "/stacks", "stacks")
- result = act(auth, stacks_state["create"],
- {"name": "T8 probe stack", "summary": "markup probe"})
- stack_id = None
- for row in slot_public(auth, "/stacks", "stacks")["stacks"]:
- if row["name"] == "T8 probe stack":
- stack_id = row["id"]
- check(stack_id is not None, "the probe stack exists")
- stack_page = http_get_page(auth, "/stacks/%s" % stack_id)
- check("data-long-form" in stack_page and "data-lf-toggle" in stack_page,
- "the Details card toggles in place (view/edit halves)")
- check("initLongFormEditors" in stack_page,
- "the stack page initialises the shared editor engine")
- check('stm-action="stack.update"' in stack_page
- and 'stm-action="stack.add_member"' in stack_page,
- "the update and add-member actions are unchanged")
- # The old side-by-side Edit card is gone; the delete is a small
- # danger button inside a flat card at the bottom.
- check("<h3>Edit</h3>" not in stack_page,
- "the duplicate side-by-side Edit card is gone")
- check('class="card card-flat"' in stack_page
- and "btn-danger" in stack_page,
- "the delete action sits in a flat danger zone")
- # Update through the in-place form's action still works.
- detail = slot_public(auth, "/stacks/%s" % stack_id, "stack")
- result = act(auth, detail["update"],
- {"stack_id": str(stack_id), "name": "T8 probe stack 2",
- "summary": "renamed in place"})
- check("saved" in (action_notify(result) or ""),
- "the in-place editor's action saves the rename")
- act(auth, slot_public(auth, "/stacks/%s" % stack_id, "stack")["delete"], {})
- check(slot_public(auth, "/stacks", "stacks")["stacks"] == [],
- "the probe stack deletes again")
|