dify/e2e/AGENTS.md

6.0 KiB

E2E

This package contains Dify's repository-level Cucumber scenarios with Playwright as the browser layer. This file owns current package architecture, runtime, session and tag semantics, seed, protocol, and cleanup contracts. The repo-local e2e-cucumber-playwright skill owns authoring and review methodology; feature-specific facts belong in the nearest feature AGENTS.md.

Commands

Run commands from the repository root. Install dependencies and browsers once with pnpm install and pnpm -C e2e e2e:install. Run only one local pnpm -C e2e e2e* process at a time because runners share ports, auth state, and log paths.

  • Existing initialized instance: pnpm -C e2e e2e
  • Reset, initialize, and run deterministic scenarios: pnpm -C e2e e2e:full
  • Tagged subset: pnpm -C e2e e2e -- --tags @smoke
  • Headed debugging: pnpm -C e2e e2e:headed -- --tags @smoke
  • External runtime preparation and run: pnpm -C e2e e2e:external:prepare, then pnpm -C e2e e2e:external
  • Reset persisted E2E state: pnpm -C e2e e2e:reset
  • Middleware lifecycle: pnpm -C e2e e2e:middleware:up and pnpm -C e2e e2e:middleware:down
  • Scoped static checks: vp check e2e

The runner reuses web/.next/BUILD_ID when present. Set E2E_FORCE_WEB_BUILD=1 to force a frontend rebuild. Use E2E_BROWSER=webkit for focused cross-browser runs and E2E_SLOW_MO=500 with a headed command for local action debugging.

Runtime Ownership

  • scripts/setup.ts owns reset, middleware, backend, and frontend startup.
  • scripts/run-cucumber.ts owns E2E orchestration and Cucumber invocation.
  • support/web-server.ts owns frontend reuse, readiness, and shutdown.
  • features/support/hooks.ts owns shared auth bootstrap, scenario lifecycle, and diagnostics.
  • features/support/world.ts owns DifyWorld, the per-scenario behavior BrowserContext, and its authenticated setup and cleanup client. Browser and API identities remain separate so unauthenticated and logout journeys cannot invalidate fixture ownership.
  • Cross-actor scenarios keep each actor in a separate BrowserContext and typed DifyWorld state so diagnostics and cleanup cover every actor.
  • features/step-definitions/ contains capability-oriented glue; common/ is reserved for genuinely cross-capability steps.
  • Step definitions that access World state use async function (this: DifyWorld, ...); arrow functions cannot receive Cucumber's bound World instance.

An uninitialized instance is installed and authenticated lazily; an initialized instance signs in and reuses authenticated state. Full runs prove reset and bootstrap during setup rather than through a Gherkin scenario. Cucumber's exit status is the behavior gate, and the runner also requires at least one testCaseStarted message so an empty tag selection cannot pass. Do not replace this gate with scenario-count baselines or skipped-scenario allowlists.

Tags And External Runtime

  • Default scenarios use shared authenticated storage state. @unauthenticated creates a clean context; @authenticated is an intent and selection tag only.
  • @prepared requires the strict post-merge seed profile.
  • @external-model and @external-tool identify scenarios that call real external runtimes. Deterministic commands exclude these tags; external commands are opt-in.
  • @microphone uses the checked-in fake audio fixture and an isolated Chromium context.
  • @browser-smoke runs focused keyboard and navigation coverage in Chromium and WebKit CI lanes.
  • Feature-owned services use their own tags. Agent v2 runtime scenarios use @agent-backend-runtime and require the explicit runtime-availability step. Set E2E_START_AGENT_BACKEND=1 to start it locally, or provide E2E_AGENT_BACKEND_URL / AGENT_BACKEND_BASE_URL.

Do not overload runtime tags to imply unrelated services or silently skip behavior when a required fixture is missing.

Browser, API, And Contract Boundaries

The action under test belongs to the browser. APIs may prepare fixtures, poll persistence, and clean up; they do not replace the user's When action. Prefer a user-observable browser result unless persisted backend state is the contract under test.

For ordinary Console JSON and representable multipart operations, use the scenario- or process-owned generated oRPC client with request and response validation enabled. Call generated operations directly. Do not add handwritten endpoint URLs, duplicate DTOs or schemas, response casts, one-to-one forwarding wrappers, mutable cross-scenario clients, or TanStack Query caching.

Keep helpers only when they own fixture construction, multi-operation orchestration, cleanup registries, invariants, eventual-consistency polling, narrowed test views, or a protocol adapter. SSE, binary downloads, redirect-only flows, external services, and infrastructure readiness may use centralized adapters under their real owner.

Validation failures are contract failures. Trace them to the backend schema owner, update api/controllers/API_SCHEMA_GUIDE.md contracts when required, regenerate @dify/contracts, and keep the scenario aligned with the product's real state owner. Do not disable validation or add fallback schemas to make E2E pass.

Seeds, Cleanup, And Diagnostics

  • Generate disposable resource names through support/naming.ts with an E2E prefix.
  • Keep deterministic upload material in fixtures/test-materials/ and resolve it through support/test-materials.ts.
  • Seed scripts own shared long-lived fixtures; scenarios own disposable resources they create and must register cleanup.
  • Use typed DifyWorld cleanup fields for known resource types and registerCleanup(...) for additional lifecycle owners. Registered callbacks run LIFO after typed cleanup queues.
  • Remove child and referencing resources before owners. Attach cleanup failures to the report instead of swallowing them.

Failures produce screenshots and HTML captures under cucumber-report/artifacts/; the HTML and Cucumber Messages reports live under cucumber-report/. Backend and frontend startup logs live under .logs/. Additional CI lanes preserve their own report and log directories.