open-swe/tests/e2e
Adam Moussa f48fc141b9
Merge upstream/main: adopt clean changes, defer #1621 modular webhooks
Sync langchain-ai/open-swe upstream/main (35 commits since fork point
f6c215f) using Option A: keep our monolithic agent/webapp.py and defer
upstream's #1621 durable-interrupt-dispatch refactor to a tracked issue.

Kept from upstream: Sonnet 5 in the model picker (re-homed to Bedrock as
bedrock_converse:us.anthropic.claude-sonnet-5, matching Opus and falling
back to Fireworks), langsmith 0.9.3, shiki grammar pre-bundling,
ensure_no_empty_msg and workflow_push_guard middleware (+ workflow-approval
prompt rule), reviewer trace context.

Deferred (entangled with #1621, tracked in #80): the modular
webhooks/{github,slack,linear} + completion/dispatch/reconcile split, the
Slack breakout-thread tool, and the Slack-context / vision-fallback /
plan-publishing features that built on the new dispatch model.

Dropped upstream's OpenAI/Gemini model options and anthropic-provider
model tests (superseded by our Bedrock + Fireworks migration); kept our
Vercel/Nitro env-var proxy over upstream's hardcoded-URL pnpm rewrite.

All 1244 tests pass; ruff clean.
2026-06-30 15:42:07 -04:00
..
screenshots feat: show queued dashboard follow-ups (#1631) 2026-06-29 11:23:45 -07:00
static feat: add Slack breakout thread tool (#1638) 2026-06-29 13:16:54 -07:00
tests feat: add Slack breakout thread tool (#1638) 2026-06-29 13:16:54 -07:00
.gitignore test(open-swe): add Playwright E2E for the Slack → PR → web handoff (#1583) 2026-06-22 12:54:46 -07:00
agent_entrypoint.py test(open-swe): add Playwright E2E for the Slack → PR → web handoff (#1583) 2026-06-22 12:54:46 -07:00
dev-mock.sh feat: plan mode with model-driven entry and collaborative review (#1580) 2026-06-23 12:06:58 -07:00
e2e_env.py feat: plan mode with model-driven entry and collaborative review (#1580) 2026-06-23 12:06:58 -07:00
fake_llm.py Merge upstream/main: adopt clean changes, defer #1621 modular webhooks 2026-06-30 15:42:07 -04:00
fakes.py feat: add Slack breakout thread tool (#1638) 2026-06-29 13:16:54 -07:00
global-setup.ts chore: switch ui to pnpm (#1645) 2026-06-30 11:29:16 -07:00
harness.py feat: add Slack breakout thread tool (#1638) 2026-06-29 13:16:54 -07:00
langgraph.e2e.json test(open-swe): add Playwright E2E for the Slack → PR → web handoff (#1583) 2026-06-22 12:54:46 -07:00
package-lock.json chore(deps): bump the minor-and-patch group across 2 directories with 4 updates (#69) 2026-06-30 14:13:01 -04:00
package.json chore(deps): bump the minor-and-patch group across 2 directories with 4 updates (#69) 2026-06-30 14:13:01 -04:00
patches.py feat: plan mode with model-driven entry and collaborative review (#1580) 2026-06-23 12:06:58 -07:00
playwright.config.ts test(open-swe): add Playwright E2E for the Slack → PR → web handoff (#1583) 2026-06-22 12:54:46 -07:00
README.md chore: switch ui to pnpm (#1645) 2026-06-30 11:29:16 -07:00

Playwright E2E — the full Slack → implement → PR → reply flow

This drives the whole happy path through two mock UIs:

  1. A user asks Open SWE to implement something in a mock Slack thread.
  2. The real agent runs (via langgraph dev): it implements the change in a local temp-dir sandbox, pushes a branch, and opens a PR on a fake GitHub.
  3. It posts the PR link back to the same Slack thread — visible in the mock UI.

What is faked vs. real

Only the LLM and the external SaaS HTTP boundaries are faked. All agent code runs for real.

Piece Real or fake
Slack webhook → process_slack_mention → run dispatch real (agent.webapp)
get_agent, deepagents loop, tools, middleware, prompt real
open_pull_request, slack_thread_reply tools real
Sandbox real local provider, rooted in a throwaway temp dir
Git remote ("GitHub") real git, a local bare repo the agent clones/pushes
The LLM fake — a scripted model (fake_llm.py) emitting a fixed tool sequence
api.github.com REST (PR create) fake (/fake-gh/...), state rendered at /mock/github
slack.com/api (post message, etc.) fake (/fake-slack/...), thread rendered at /mock/slack
GitHub App token mint, api.github.com/user identity stubbed (offline)

The fake GitHub/Slack stores are the single source of truth the mock UIs render, so what Playwright asserts on is exactly what the real agent produced.

Files

  • e2e_env.py — env + constants set before any agent.* import (sandbox=local, fake API URLs, isolated GIT_CONFIG_GLOBAL, bot-token-only mode).
  • fake_llm.py — the scripted BaseChatModel (the only faked agent piece).
  • patches.py — monkeypatches the boundaries (LLM, GitHub/Slack URLs, token mint).
  • agent_entrypoint.py — langgraph agent graph: applies patches, re-exports the real traced_agent.
  • harness.py — langgraph http.app: the real agent.webapp plus the fake GitHub/Slack APIs, the mock UIs, and the control/compose endpoints.
  • fakes.py — in-memory PR/Slack stores + git seeding of the bare remote.
  • langgraph.e2e.json — dev-server config pointing at the two entrypoints above.
  • static/{slack,github}.html — the mock Slack/GitHub UIs (external SaaS we can't run locally). The dashboard is not mocked — it's the real ui/ app.
  • global-setup.ts — builds the real ui/ SPA (once) so the harness can serve it.

The dashboard — the real ui/ app

The dashboard is not mocked. The bot's "Open in Web" link (DASHBOARD_BASE_URL/agents/{thread_id}) loads the actual built ui/ React app — served same-origin from the harness so the session cookie and /dashboard/api/* calls work without CORS. The signed session cookie is real (minted via /control/login), so per-user authorization is genuine; the only extra fake is the OAuth-token store (an external credential).

The UI is built by global-setup.ts with VITE_DASHBOARD_API_BASE_URL pointed at the harness. It builds once; set E2E_FORCE_UI_BUILD=1 to rebuild (e.g. after a UI change or port change). Requires Corepack with pnpm enabled.

Run

cd tests/e2e
npm install
npx playwright install chromium
npx playwright test          # boots langgraph dev automatically, then runs

Watch it in human time:

SLOW_MO=700 npx playwright test --headed

Artifacts (replay a run)

Every test records a trace (DOM-snapshot timeline + network + console + source) and a video; failures also get a screenshot. Locally they land in test-results/<test>/ and are embedded in playwright-report/:

npx playwright show-report                       # browse runs; each has a Trace tab
npx playwright show-trace test-results/<test>/trace.zip   # open one trace directly

In CI the Playwright E2E job uploads both playwright-report/ and test-results/ as the playwright-report artifact on the run. Download it, then npx playwright show-report <unzipped-dir> (or drag a trace.zip onto https://trace.playwright.dev) to replay.

Poke at it by hand (from the repo root):

uv run langgraph dev --config tests/e2e/langgraph.e2e.json --port 2024 \
  --no-browser --allow-blocking --no-reload
# open http://127.0.0.1:2024/mock/slack  and  /mock/github