aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rwxr-xr-xtests/e2e.sh73
-rwxr-xr-xtests/pi-live.sh23
2 files changed, 92 insertions, 4 deletions
diff --git a/tests/e2e.sh b/tests/e2e.sh
index 7f72e3c..e73dc73 100755
--- a/tests/e2e.sh
+++ b/tests/e2e.sh
@@ -285,13 +285,15 @@ cat >"$DIR/ext-harness.mjs" <<'JS'
// ("agent_start"), runs commands ("/importance high"), and asks for argument
// completions ("complete:importance h"). Custom session entries persist in
// $SESSION_ENTRIES, so a later run reads them back as the session; what the
-// extension shows and emits goes to $EXT_LOG.
+// extension shows and emits goes to $EXT_LOG. The input box holds
+// $EDITOR_TEXT; the session file is $SESSION_FILE.
import fs from "node:fs";
const ext = await import(process.env.EXTENSION);
const on = {}, commands = {};
const store = process.env.SESSION_ENTRIES;
const entries = store && fs.existsSync(store) ? JSON.parse(fs.readFileSync(store, "utf8")) : [];
const log = (line) => process.env.EXT_LOG && fs.appendFileSync(process.env.EXT_LOG, line + "\n");
+let editor = process.env.EDITOR_TEXT ?? "";
ext.default({
on: (name, fn) => (on[name] = fn),
registerCommand: (name, command) => (commands[name] = command),
@@ -304,8 +306,13 @@ ext.default({
const ctx = {
mode: "tui",
isIdle: () => true,
- sessionManager: { getEntries: () => entries, getSessionFile: () => undefined },
- ui: { setStatus: (key, text) => log(`status ${key} ${text ?? ""}`), notify: (text) => log(`notify ${text}`) },
+ sessionManager: { getEntries: () => entries, getSessionFile: () => process.env.SESSION_FILE },
+ ui: {
+ setStatus: (key, text) => log(`status ${key} ${text ?? ""}`),
+ notify: (text) => log(`notify ${text}`),
+ getEditorText: () => editor,
+ setEditorText: (text) => { editor = text; log(`editor ${text}`); },
+ },
};
for (const arg of process.argv.slice(2)) {
if (arg.startsWith("/")) {
@@ -1032,6 +1039,66 @@ expect "the menu: only those by hand, not clickable" "mb --print | grep '^⟳'"
"$(printf '⟳ %s Agents Need /reload\ttheir dingger extension is too old to reload them\t(disabled)' "$BY_HAND")"
t kill-session -t ext
+# --- 24. forgotten drafts -----------------------------------------------------
+# pi's extension reports the draft in its input box; dingger marks it where
+# the user is not looking, and keeps it in a file per session that comes back
+# with the session. (Typing into a real pi: tests/pi-live.sh.)
+echo "24. forgotten drafts"
+draft_report() { # pane draft
+ python3 - "$HOOK" "$1" "$2" <<'PY'
+import json, socket, sys
+params = {"pane_id": sys.argv[2], "agent": "pi", "draft": sys.argv[3]}
+s = socket.socket(socket.AF_UNIX)
+s.connect(sys.argv[1])
+s.sendall((json.dumps({"method": "pane.report_agent", "params": params}) + "\n").encode())
+s.close()
+PY
+}
+t new-session -d -s dr -n d1 -c "$DIR" "$DIR/bin/pi"
+t new-window -d -t dr: -n d2 -c "$DIR" "$DIR/bin/pi"
+t new-window -d -t dr: -n away -c "$DIR" "sleep 100000"
+D1=$(t display -p -t dr:d1 '#{pane_id}'); D2=$(t display -p -t dr:d2 '#{pane_id}')
+look "$(t display -p -t dr:away '#{pane_id}')"
+for p in "$D1" "$D2"; do report "$p" working ""; report "$p" idle ""; expect "dr: $p done" "opt $p @agent_state" done; done
+look "$D1"; sleep 1; look "$D2"; sleep 1; look "$(t display -p -t dr:away '#{pane_id}')"
+expect "dr: both idle" "opt $D1 @agent_state; opt $D2 @agent_state" "idle
+idle"
+draft_report "$D2" "$(printf 'fix the\n sidecar;')"
+expect "a draft, as one line, its trailing ; kept" "opt $D2 @dingger_draft" "fix the sidecar;"
+expect "pane options after it still written" "opt $D2 @agent_state" idle
+drow() { mb --print | grep -F "dr · $1 ·" | cut -f2; }
+expect "menu: marked ✎" "drow d2" "✎ pi"
+expect "first among idle agents" "mb --print | grep -F 'dr · d' | cut -f3 | cut -d' ' -f3 | tr '\n' ' '" "d2 d1 "
+expect "search finds it by its draft" "mb --print-search sidecar | head -1 | cut -f3" "dr · d2 · pi"
+expect "dingger status marks it" "$DINGGER status | grep -F 'dr · d2' | grep -c '✎ '" 1
+look "$D2"
+expect "where you look, no mark: you may be typing it" "drow d2" "pi"
+expect "nor in dingger status" "$DINGGER status | grep -F 'dr · d2' | grep -c '✎ '" 0
+look "$D1"
+draft_report "$D2" ""
+expect "a cleared draft: gone" "opt $D2 @dingger_draft" ""
+expect "menu: no mark" "drow d2" "pi"
+draft_report "$D2" ";"
+expect "a draft of just ;" "opt $D2 @dingger_draft" ";"
+report "$D2" working ""
+expect "a report without a draft keeps it" "opt $D2 @dingger_draft" ";"
+if command -v node >/dev/null; then
+ DRAFTS="$(dirname "$HOOK")/drafts"
+ EDITOR_TEXT="half a thought" SESSION_FILE="$DIR/sessions/s1.jsonl" iext "$D1" session_start
+ expect "the extension reports its draft" "opt $D1 @dingger_draft" "half a thought"
+ expect "and keeps it for the session" "cat $DRAFTS/s1.jsonl.draft" "half a thought"
+ : >"$EXTLOG"; SESSION_FILE="$DIR/sessions/s1.jsonl" iext "$D1" session_start
+ expect "the session again: its draft back in the input box" "grep '^editor' $EXTLOG" "editor half a thought"
+ expect "and reported" "opt $D1 @dingger_draft" "half a thought"
+ : >"$EXTLOG"; EDITOR_TEXT="typed meanwhile" SESSION_FILE="$DIR/sessions/s1.jsonl" iext "$D1" session_start
+ expect "an input box with text keeps it" "grep -c '^editor' $EXTLOG" 0
+ expect "and that is the draft now" "cat $DRAFTS/s1.jsonl.draft" "typed meanwhile"
+ : >"$EXTLOG"; SESSION_FILE="$DIR/sessions/s2.jsonl" iext "$D1" session_start
+ expect "another session: none of it" "grep -c '^editor' $EXTLOG" 0
+ expect "dr: no draft reported" "opt $D1 @dingger_draft" ""
+fi
+t kill-session -t dr
+
# Every daemon this run started must stop with it.
kill "$DAEMON_PID" "$DAEMON2_PID" 2>/dev/null
expect "the test daemons stop" "ps -o pid= -p $DAEMON_PID,$DAEMON2_PID | wc -l | tr -d ' '" 0
diff --git a/tests/pi-live.sh b/tests/pi-live.sh
index 4ea4caf..6dff42f 100755
--- a/tests/pi-live.sh
+++ b/tests/pi-live.sh
@@ -169,6 +169,10 @@ expect "and the turn ends" "st agent" done 80
t send-keys -t main:agent -l "half typed"
sleep 0.5
expect "a draft: it refuses" "ask_reload" '{"ok":false,"reason":"draft"}'
+draft() { t show -pv -t main:agent @dingger_draft 2>/dev/null; }
+draft_file() { echo "$D/drafts/$(basename "$(t show -pv -t main:agent @agent_session)").draft"; }
+expect "the draft reaches dingger" "draft" "half typed" 30
+expect "and is kept for the session" "cat \"\$(draft_file)\"" "half typed" 30
# Through dingger: refused for the draft, retried until it can.
"$DINGGER" reload-agents >"$D/reload.out"
expect "reload-agents: not yet" "head -1 $D/reload.out" "reloaded 0 of 1 agent on an old dingger extension"
@@ -176,6 +180,8 @@ expect "it says why it waits" "sed -n 2p $D/reload.out" "the other 1 reload once
expect "the daemon was told no: a draft" "sed 's/\x1b\[[0-9;]*m//g' $D/daemon.log | grep -c 'reload: not now target=main:agent reason=draft'" 1 30
expect "the draft is untouched" "t capture-pane -p -t main:agent | grep -c 'half typed'" 1
for _ in $(seq 10); do t send-keys -t main:agent BSpace; done
+expect "deleted: no draft" "draft" "" 30
+expect "nor its file" "[ -e \"\$(draft_file)\" ] && echo left || echo gone" gone 30
expect "once the input box is empty: reloaded" "t show -pv -t main:agent @dingger_extension 2>/dev/null" "$CURRENT" 150
expect "by the daemon" "reloads reloading" 1 30
expect "a new instance (a new socket)" "[ \"\$(ctl)\" != \"$SOCK1\" ] && [ -S \"\$(ctl)\" ] && echo yes" yes
@@ -183,9 +189,13 @@ expect "the old socket is gone" "[ -e \"$SOCK1\" ] && echo left || echo gone" go
expect "nothing left to reload" "t show -gv @dingger_outdated 2>/dev/null" 0 30
expect "still done (unseen)" "st agent" done
expect "no message reached the model" "grep -c dingger-reload \"\$(t show -pv -t main:agent @agent_session)\"" 0
-type_line agent "slow 1 after"
+t send-keys -t main:agent -l "slow 1 after"
+expect "typed, then a pause: a draft" "draft" "slow 1 after" 30
+t send-keys -t main:agent Enter
expect "it still works: working" "st agent" working 50
expect "and done" "st agent" done 60
+expect "a message sent is no draft" "draft" "" 30
+expect "nor kept" "[ -e \"\$(draft_file)\" ] && echo left || echo gone" gone 30
echo "2. screen detection (pi without the extension)"
t send-keys -t main:plain "PI_CODING_AGENT_DIR=$D/agent-plain pi $NO_MCP" Enter
@@ -200,6 +210,10 @@ echo "3. restore"
SESSION=$(t show -pv -t main:agent @agent_session)
latest() { ls "$D"/state/snapshots/*.json 2>/dev/null | tail -1; }
expect "the snapshot has the agent's session" "grep -c \"\\\"session\\\": \\\"$SESSION\\\"\" \"\$(latest)\" 2>/dev/null" 1 80
+# A draft left behind when tmux goes away.
+# (`\;`: send-keys would end its command at a trailing ;)
+t send-keys -t main:agent -l 'an unsent idea\;'
+expect "a draft before the crash" "t show -pv -t main:agent @dingger_draft 2>/dev/null" "an unsent idea;" 30
t kill-server
expect "the layout becomes the restore candidate" "python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))[\"status\"])' $D/state/restore.json 2>/dev/null" offered 80
"$DINGGER" restore >"$D/restore.out" 2>&1
@@ -208,10 +222,17 @@ expect "resumed 1 of 1 agents" "grep -c '^resumed 1 of 1 agents' $D/restore.out"
expect "it runs again, reporting the same session" "t show -pv -t main:agent @agent_session 2>/dev/null" "$SESSION" 150
expect "with its conversation" "t capture-pane -p -t main:agent -S - | grep -c 'slow 3 hello'" 1 50
expect "and its importance" "t show -pv -t main:agent @dingger_importance 2>/dev/null" high 50
+expect "and its draft, back in the input box" "t capture-pane -p -t main:agent | grep -c '^an unsent idea;'" 1 50
+expect "reported again" "t show -pv -t main:agent @dingger_draft 2>/dev/null" "an unsent idea;" 30
echo "4. exit"
+LAST_DRAFT=$(draft_file)
+expect "its draft is kept" "cat $LAST_DRAFT" "an unsent idea;"
+# Deleted, then quit at once: before the input box is read again.
+for _ in $(seq 20); do t send-keys -t main:agent BSpace; done
t send-keys -t main:agent C-d
expect "the agent is gone from the pane" "t show -pv -t main:agent @agent 2>/dev/null" "" 100
+expect "a draft deleted before quitting is not kept" "[ -e $LAST_DRAFT ] && echo left || echo gone" gone 30
echo
echo "$PASSES passed, $FAILS failed"