diff --git a/src/gateway/sync.test.ts b/src/gateway/sync.test.ts index 580b0c9b..f062ffed 100644 --- a/src/gateway/sync.test.ts +++ b/src/gateway/sync.test.ts @@ -43,8 +43,8 @@ describe('syncToR2', () => { const { sandbox, startProcessMock } = createMockSandbox(); startProcessMock .mockResolvedValueOnce(createMockProcess('s3fs on /data/moltbot type fuse.s3fs\n')) - .mockResolvedValueOnce(createMockProcess('')) // No openclaw.json - .mockResolvedValueOnce(createMockProcess('')); // No clawdbot.json either + .mockResolvedValueOnce(createMockProcess('', { exitCode: 1 })) // No openclaw.json + .mockResolvedValueOnce(createMockProcess('', { exitCode: 1 })); // No clawdbot.json either const env = createMockEnvWithR2(); diff --git a/src/gateway/sync.ts b/src/gateway/sync.ts index 910f9b37..63808c47 100644 --- a/src/gateway/sync.ts +++ b/src/gateway/sync.ts @@ -43,21 +43,15 @@ export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise { + await page.goto('$WORKER_URL/?token=$TOKEN'); +}" # Wait for pairing required message (worker shows loading screen first, then UI loads) ./pw --session=moltworker-e2e run-code "async page => { await page.waitForSelector('text=Pairing required', { timeout: 480000 }); diff --git a/test/e2e/_teardown.txt b/test/e2e/_teardown.txt index 6e914b49..ae2952d7 100644 --- a/test/e2e/_teardown.txt +++ b/test/e2e/_teardown.txt @@ -4,19 +4,13 @@ dump gateway logs for debugging WORKER_URL=$(cat "$CCTR_FIXTURE_DIR/worker-url.txt" 2>/dev/null || echo "") if [ -n "$WORKER_URL" ]; then PROCS=$(./curl-auth -s "$WORKER_URL/debug/processes" 2>/dev/null || echo "") - PROC_ID=$(echo "$PROCS" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + PROC_ID=$(echo "$PROCS" | jq -r '[.processes[] | select(.command | contains("start-openclaw"))][0].id // empty' 2>/dev/null) if [ -n "$PROC_ID" ]; then echo "=== Gateway process logs ($PROC_ID) ===" - ./curl-auth -s "$WORKER_URL/debug/logs?id=$PROC_ID" 2>/dev/null | python3 -c " -import sys, json -try: - d = json.load(sys.stdin) - if d.get('stdout'): print('STDOUT:', d['stdout'][-3000:]) - if d.get('stderr'): print('STDERR:', d['stderr'][-3000:]) -except: print('Failed to parse logs') -" || echo "Failed to fetch logs" + LOGS=$(./curl-auth -s "$WORKER_URL/debug/logs?id=$PROC_ID" 2>/dev/null) + echo "$LOGS" | jq -r '"STATUS: \(.process_status)\nSTDOUT: \(.stdout)\nSTDERR: \(.stderr)"' 2>/dev/null || echo "Failed to parse logs" else - echo "No gateway process found" + echo "No start-openclaw.sh process found" echo "Processes: $PROCS" fi else diff --git a/test/e2e/fixture/start-browser b/test/e2e/fixture/start-browser index 6338db6c..909a527c 100755 --- a/test/e2e/fixture/start-browser +++ b/test/e2e/fixture/start-browser @@ -4,17 +4,12 @@ set -e SESSION_NAME="moltworker-e2e" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Support running directly (not via cctr) if [ -z "$CCTR_FIXTURE_DIR" ]; then CCTR_FIXTURE_DIR="/tmp/e2e-cloud-manual" fi -# Stop and delete any existing session -playwright-cli session-stop "$SESSION_NAME" >/dev/null 2>&1 || true -playwright-cli session-delete "$SESSION_NAME" >/dev/null 2>&1 || true - # Build the args GLOBAL_ARGS=("--session=$SESSION_NAME") @@ -22,22 +17,25 @@ if [ "${PLAYWRIGHT_HEADED:-}" = "1" ] || [ "${PLAYWRIGHT_HEADED:-}" = "true" ]; GLOBAL_ARGS+=("--headed") fi -# Open the browser to a blank page first -playwright-cli "${GLOBAL_ARGS[@]}" open "about:blank" >/dev/null 2>&1 & -sleep 2 +# Open the browser to a blank page first (output to stderr to keep stdout clean for cctr) +playwright-cli "${GLOBAL_ARGS[@]}" open "about:blank" >&2 & +sleep 20 # Read Access credentials CF_ACCESS_CLIENT_ID=$(cat "$CCTR_FIXTURE_DIR/cf-access-client-id.txt" 2>/dev/null || echo "") CF_ACCESS_CLIENT_SECRET=$(cat "$CCTR_FIXTURE_DIR/cf-access-client-secret.txt" 2>/dev/null || echo "") if [ -n "$CF_ACCESS_CLIENT_ID" ] && [ -n "$CF_ACCESS_CLIENT_SECRET" ]; then - # Set extra HTTP headers for Access authentication + # Set extra HTTP headers for Access authentication (output to stderr). + # IMPORTANT: All subsequent navigation MUST use 'run-code page.goto()' instead of 'open', + # because 'open' creates a new browser process which loses these headers. playwright-cli "${GLOBAL_ARGS[@]}" run-code "async page => { await page.context().setExtraHTTPHeaders({ 'CF-Access-Client-Id': '$CF_ACCESS_CLIENT_ID', 'CF-Access-Client-Secret': '$CF_ACCESS_CLIENT_SECRET' }); - }" >/dev/null 2>&1 + }" >&2 fi +sleep 1 # Let stderr flush before stdout echo "ready" diff --git a/test/e2e/pairing_and_conversation.txt b/test/e2e/pairing_and_conversation.txt index 7ae70dcb..fb700a47 100644 --- a/test/e2e/pairing_and_conversation.txt +++ b/test/e2e/pairing_and_conversation.txt @@ -4,7 +4,9 @@ navigate to admin page to approve device === TOKEN=$(cat "$CCTR_FIXTURE_DIR/gateway-token.txt") WORKER_URL=$(cat "$CCTR_FIXTURE_DIR/worker-url.txt") -./pw --session=moltworker-e2e open "$WORKER_URL/_admin/?token=$TOKEN" +./pw --session=moltworker-e2e run-code "async page => { + await page.goto('$WORKER_URL/_admin/?token=$TOKEN'); +}" --- === @@ -41,7 +43,9 @@ navigate back to main chat page === TOKEN=$(cat "$CCTR_FIXTURE_DIR/gateway-token.txt") WORKER_URL=$(cat "$CCTR_FIXTURE_DIR/worker-url.txt") -./pw --session=moltworker-e2e open "$WORKER_URL/?token=$TOKEN" +./pw --session=moltworker-e2e run-code "async page => { + await page.goto('$WORKER_URL/?token=$TOKEN'); +}" --- === @@ -53,6 +57,18 @@ wait for chat interface to load }" --- +=== +send /models command +%require +=== +./pw --session=moltworker-e2e run-code "async page => { + const textarea = await page.waitForSelector('textarea'); + await textarea.fill('/models'); + const btn = await page.waitForSelector('button:has-text(\"Send\")'); + await btn.click(); +}" +--- + === type math question into chat %require