| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- """The T4 lists bundle (goal 41 task 4): the tasks/goals/projects/stacks
- list pages on the Spry conversion — faceted status chips with counts on
- /tasks (default view = open work) and /goals, the create-form expanders
- replaced by native <dialog> modals (openAppDialog helper in app.js),
- the goals Tasks column as readable badges, and the projects list's
- folded repo cell. Markup-level: http_get_page fetches the compiled
- page; the counts are cross-checked against the slot data the chips
- count over."""
- from harness import * # noqa: F401,F403 — the shared wire helpers
- def run(ctx):
- auth, fake, fake_id, fake_token = ctx.auth, ctx.fake, ctx.fake_id, ctx.fake_token
- OPEN = ("queued", "assigned", "working", "blocked", "suspended")
- def status_counts():
- """Per-status task counts from the /tasks slot (the chip
- numbers count exactly these rows)."""
- rows = slot_public(auth, "/tasks", "tasks")["tasks"]
- counts = {}
- for row in rows:
- counts[row["status"]] = counts.get(row["status"], 0) + 1
- counts["all"] = len(rows)
- counts["open"] = sum(n for s, n in counts.items()
- if s in OPEN)
- return counts
- def seed_tasks():
- conn = db()
- for i, status in enumerate(("queued", "working", "blocked", "done",
- "done", "failed", "draft")):
- conn.execute(
- "insert into tasks (project_id, title, status, priority,"
- " created_at) values (1, ?, ?, 5, strftime('%s','now'))",
- ("T4 chip probe %d" % i, status))
- conn.commit()
- conn.close()
- print("== /tasks: faceted status chips with counts ==")
- seed_tasks()
- counts = status_counts()
- check(counts["open"] == 3 and counts["all"] >= 7,
- "the fixture shapes the counts (open %d / all %d)"
- % (counts["open"], counts["all"]))
- tasks_page = http_get_page(auth, "/tasks")
- check("taskChipClass" in tasks_page and "taskStatusCount" in tasks_page
- and "TASK_OPEN_STATUSES" in tasks_page,
- "the tasks page ships the chip engine (class/count/open-set)")
- check(tasks_page.index("goal-strip") < tasks_page.index("<table"),
- "the chip strip sits above the queue table")
- check("window.setTaskStatusFilter('open')" in tasks_page
- and "window.setTaskStatusFilter('done')" in tasks_page,
- "every chip drives the shared setTaskFilter engine")
- # The default view is open work: the status filter initialises to
- # 'open' and taskRowVisible understands the open set.
- check("taskFilters.status = 'open'" in tasks_page
- and "TASK_OPEN_STATUSES.indexOf(row.status) === -1" in tasks_page,
- "the default filter is the open-work set (queued..suspended)")
- check("filter-project" in tasks_page and "filter-goal" in tasks_page,
- "the project and goal selects stay (chips drive the status axis)")
- check('id="filter-status"' not in tasks_page,
- "the old status select is gone")
- print("== /tasks: the create form lives in a dialog ==")
- check('id="task-create-dialog"' in tasks_page
- and "<dialog" in tasks_page,
- "the create form is a native dialog")
- check(tasks_page.index("<dialog") > tasks_page.index("</table>"),
- "the dialog markup sits after the table, opened by the button")
- check('onclick="window.openAppDialog(\'task-create-dialog\')"' in tasks_page,
- "the + New task button opens the dialog through the shared helper")
- # The form inside the dialog is the SAME form: same action, same
- # fields (the B6b branch fields, the mode select, auto-enqueue).
- dialog_html = tasks_page[tasks_page.index('id="task-create-dialog"'):]
- check('stm-action="tasks.create"' in dialog_html
- and 'name="task_branch"' in dialog_html
- and 'name="branch_from"' in dialog_html
- and 'id="task-mode"' in dialog_html
- and 'id="task-enqueue-after"' in dialog_html
- and "No project (standalone)" in dialog_html,
- "the dialog carries the unchanged create form (action + fields)")
- check('class="dialog-titlebar"' in dialog_html
- and 'class="dialog-footer"' in dialog_html,
- "the dialog uses Spry's dialog classes (titlebar/footer)")
- # Semantics still work end to end: create through the action the
- # dialog form posts (new tasks land in draft).
- before = counts["draft"]
- act(auth, slot_public(auth, "/tasks", "tasks")["create"], {
- "project_id": "1", "title": "T4 dialog create task", "priority": "5"})
- after = status_counts()
- check(after["draft"] == before + 1,
- "the dialog form's action still creates (draft %d -> %d)"
- % (before, after["draft"]))
- print("== app.js: the shared dialog helper ==")
- status, app_js = http("GET", "/_statum/resource/app.js")
- check(status == 200 and "window.openAppDialog" in app_js
- and "window.closeAppDialog" in app_js,
- "the shared dialog helper ships in app.js")
- check("onNotifyHandler" in app_js and "dialog.close()" in app_js,
- "the helper closes the dialog on the action's notify")
- print("== /goals: chips + readable task badges + create dialog ==")
- goals_page = http_get_page(auth, "/goals")
- check("goalStatusCount" in goals_page and "goalChipClass" in goals_page
- and "setGoalStatusFilter" in goals_page,
- "the goals list ships the same chip engine")
- check("goalRowVisible" in goals_page,
- "the goals rows filter client-side like the queue")
- # The four-count muted string is gone; badges carry the shape.
- check("draft / " not in goals_page
- and "running_tasks + " in goals_page,
- "the Tasks column renders the total badge from the counts")
- check('badge-warning" stm-if="goal.running_tasks > 0"' in goals_page,
- "running tasks surface as a warning badge")
- check('id="goal-create-dialog"' in goals_page
- and 'stm-action="goals.create"' in goals_page,
- "the goal create form lives in a dialog")
- goal_dialog = goals_page[goals_page.index('id="goal-create-dialog"'):]
- check('id="goal-brief"' in goal_dialog and "textarea" in goal_dialog
- and "required" in goal_dialog,
- "the required brief textarea is inside the dialog (visible on open)")
- print("== /projects: the folded repo cell + create dialog ==")
- projects_page = http_get_page(auth, "/projects")
- check('id="project-create-dialog"' in projects_page
- and 'stm-action="projects.create"' in projects_page
- and 'onclick="window.openAppDialog(\'project-create-dialog\')"' in projects_page,
- "the project create form lives in a dialog behind the button")
- check("<th>Repo</th>" in projects_page and "<th>Git URL</th>" not in projects_page,
- "the Git URL column folded into one Repo cell")
- check("repo-url" in projects_page and "truncate" in projects_page,
- "the repo cell truncates the clone URL (branch under it)")
- print("== /stacks: the create dialog ==")
- stacks_page = http_get_page(auth, "/stacks")
- check('id="stack-create-dialog"' in stacks_page
- and 'stm-action="stacks.create"' in stacks_page
- and 'onclick="window.openAppDialog(\'stack-create-dialog\')"' in stacks_page,
- "the stack create form lives in a dialog behind the button")
- check("<details>" not in stacks_page and "<details>" not in projects_page,
- "no create expander remains on stacks or projects")
|