SKILL.md
SKILL.mdBrowse 4 files
3,232 tokens
13,437 bytes
Token encoding: o200k_base
Snapshot 595cd72
1---2name: e2e-testing3description: Write and review Playwright E2E tests for Langflow. Trigger when the user asks to write, fix, or review E2E tests, spec files, Playwright tests, or integration tests that exercise the full UI. Also trigger when modifying data-testid attributes, test helpers in tests/utils/, or fixture configuration.4---5 6# Langflow E2E Testing (Playwright)7 8## When to Apply9 10- User asks to **write E2E tests** for a feature or flow11- User asks to **fix a failing E2E test**12- User asks to **review E2E test coverage**13- User modifies `data-testid` attributes in components (may break existing tests)14- User changes test utilities in `src/frontend/tests/utils/`15 16**Do NOT apply** when:17- User asks about unit tests (use `frontend-testing` skill for Jest)18- User asks about backend tests (use `backend-code-review` skill for pytest)19 20## Tech Stack21 22| Tool | Version | Purpose |23|------|---------|---------|24| Playwright | 1.59.1 | E2E test runner + browser automation |25| Chromium | (bundled) | Default browser (Firefox/Safari disabled) |26| Custom fixtures | `tests/fixtures.ts` | Auto-detects API errors and flow execution failures |27 28## Key Commands29 30```bash31# Run all E2E tests32npx playwright test33 34# Run tests filtered by tag35npx playwright test --grep "@release"36npx playwright test --grep "@workspace"37npx playwright test --grep "@starter-projects"38 39# Run a specific test file40npx playwright test tests/core/features/run-flow.spec.ts41 42# Debug mode (headed browser + step through)43npx playwright test --debug44 45# Show HTML report after run46npx playwright show-report47 48# Update snapshots (if used)49npx playwright test --update-snapshots50```51 52## Configuration53 54**File**: `src/frontend/playwright.config.ts`55 56| Setting | Value | Why |57|---------|-------|-----|58| `fullyParallel` | `true` | Tests run in parallel for speed |59| `timeout` | 5 minutes | Flow builds can be slow; prevents false timeouts |60| `retries` | 3 (local), 2 (CI) | Flaky network/rendering issues; retries catch them |61| `workers` | 2 | Balances speed and resource usage |62| `actionTimeout` | 20s | Individual action timeout (click, fill, etc.) |63| `trace` | `on-first-retry` | Captures trace on failures for debugging |64| `baseURL` | `http://localhost:3000` | Vite dev server |65 66**WebServer**: Playwright auto-starts backend (uvicorn on 7860) + frontend (npm start on 3000).67 68## Directory Structure69 70```71src/frontend/tests/72├── fixtures.ts # Custom test fixture with error detection73├── globalTeardown.ts # Cleanup (removes temp DB after tests)74├── core/75│ ├── features/ # Main feature tests (run-flow, playground, etc.)76│ ├── integrations/ # Starter project / template tests77│ ├── regression/ # Bug regression tests78│ └── unit/ # Component-level Playwright tests79├── extended/80│ ├── features/ # Extended features (MCP, auto-save, etc.)81│ ├── integrations/ # Extended integrations82│ └── regression/ # Extended regressions83└── utils/ # 37+ shared helper functions84```85 86## File Naming87 88- **kebab-case** with `.spec.ts` suffix: `run-flow.spec.ts`, `playground.spec.ts`, `flow-lock.spec.ts`89- Template tests may use spaces: `Document QA.spec.ts`, `Social Media Agent.spec.ts`90- Sharded tests for parallelization: `chatInputOutputUser-shard-0.spec.ts`91 92> **Note**: E2E tests use `.spec.ts` (Playwright convention). Unit tests use `.test.tsx` (Jest convention). Do not mix them.93 94## Test Anatomy95 96### Basic Test97 98```typescript99import { expect, test } from "../../fixtures";100import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";101 102test(103 "user should be able to run a flow successfully",104 { tag: ["@release", "@workspace"] },105 async ({ page }) => {106 await awaitBootstrapTest(page);107 108 // Arrange: Create a flow109 await page.getByTestId("blank-flow").click();110 111 // Act: Add components and run112 await page.getByTestId("sidebar-search-input").fill("Chat Output");113 // ... setup ...114 115 // Assert: Verify result116 await expect(page.getByTestId("build-status-success")).toBeVisible({ timeout: 30000 });117 },118);119```120 121### With test.describe122 123```typescript124test.describe("Flow Lock Feature", () => {125 test(126 "should lock and unlock a flow",127 { tag: ["@release", "@api"] },128 async ({ page }) => {129 // ...130 },131 );132 133 test(134 "should prevent editing when locked",135 { tag: ["@release"] },136 async ({ page }) => {137 // ...138 },139 );140});141```142 143### With Serial Mode (tests that depend on order)144 145```typescript146test.describe.configure({ mode: "serial" });147 148test("step 1: create flow", async ({ page }) => { /* ... */ });149test("step 2: edit flow", async ({ page }) => { /* ... */ });150test("step 3: delete flow", async ({ page }) => { /* ... */ });151```152 153### With Event Delivery Modes (streaming/polling/direct)154 155```typescript156import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";157 158withEventDeliveryModes(159 "Document Q&A should work",160 { tag: ["@release", "@starter-projects"] },161 async ({ page }) => {162 // This test runs 3 times: streaming, polling, direct163 // Each mode is configured automatically via route interception164 },165);166```167 168## Tags System169 170Every test MUST be tagged with `@release` — the release run greps for it, so an171untagged or wrongly-tagged spec silently drops out of release coverage. Add the172domain tag(s) below on top of `@release` (a test can have more than one). These173six are the only allowed tags; do not invent new ones.174 175| Tag | Purpose | When to Use |176|-----|---------|-------------|177| `@release` | Part of the release run (required on every spec) | All tests |178| `@workspace` | Workspace/flow management | Creating, editing, deleting flows |179| `@api` | API-dependent features | Tests that call backend endpoints |180| `@database` | Database operations | Tests involving persistence |181| `@components` | Component-level tests | Individual component behavior |182| `@starter-projects` | Template/starter project tests | Pre-built flow templates |183 184```typescript185// Right: tag your test186test("my feature test", { tag: ["@release", "@workspace"] }, async ({ page }) => { ... });187 188// Wrong: no tags — test can't be filtered189test("my feature test", async ({ page }) => { ... });190```191 192## Custom Fixtures: Error Detection193 194**Always import `test` and `expect` from `../../fixtures`, NOT from `@playwright/test`.**195 196```typescript197// Right198import { expect, test } from "../../fixtures";199 200// Wrong — bypasses error detection201import { expect, test } from "@playwright/test";202```203 204Why: The custom fixture automatically monitors all `/api/` responses and fails the test if:205- HTTP 400, 404, 422, or 500 errors occur206- Flow execution returns `error: true` in event streams207- Python exceptions appear in streamed responses208 209To opt-in to expected errors (e.g., testing error handling):210```typescript211test("should show error on invalid input", { tag: ["@release"] }, async ({ page }) => {212 page.allowFlowErrors(); // Allow flow errors for this test213 // ... test that expects errors ...214});215```216 217## Selector Strategy218 219### Priority (in order of preference)220 2211. **`getByTestId`** — Most stable, used 95% of the time in Langflow2222. **`getByRole`** — For buttons, headings, and form elements2233. **`getByText`** — For visible text content2244. **`waitForSelector`** — For CSS selectors and dynamic elements2255. **`locator`** — For complex selectors (CSS, XPath)226 227### Common data-testid Patterns228 229**Canvas & Navigation:**230- `blank-flow` — New blank flow button231- `sidebar-search-input` — Component search232- `canvas_controls_dropdown` — Canvas controls menu233- `fit_view`, `zoom_out`, `zoom_in` — Canvas controls234- `react-flow-id` — ReactFlow canvas container235 236**Component Fields:**237- `popover-anchor-input-{fieldname}` — Input field for a component parameter238- `input-chat-playground` — Playground chat input239- `div-chat-message` — Chat message in playground240 241**Actions:**242- `add-component-button-{component}` — Add component to canvas243- `button-send` — Send chat message244- `button_run_{component}` — Run specific component245- `publish-button`, `save-flow-button` — Flow actions246- `edit-fields-button` — Toggle inspection panel field editor247 248**Modals & Panels:**249- `modal-title` — Modal heading250- `icon-Globe` — Global variables251- `icon-Lock` — Flow lock toggle252- `session-selector` — Playground session switcher253 254### Important: Global Variables and Badges255 256When a component field has a global variable selected (`load_from_db: true` + `value: "OPENAI_API_KEY"`), the field renders a **badge** instead of an `<input>` element. This means `getByTestId("popover-anchor-input-api_key")` will NOT find the element — it doesn't exist in the DOM.257 258Templates with global variables pre-selected: Market Research, Price Deal Finder, Research Agent.259Templates without (input IS rendered): Instagram Copywriter.260 261## Core Helper Functions262 263Located in `src/frontend/tests/utils/`:264 265| Function | What it Does | When to Use |266|----------|-------------|-------------|267| `awaitBootstrapTest(page)` | Waits for app to fully load | **Start of every test** |268| `initialGPTsetup(page)` | Full setup: adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes | Tests that need OpenAI configured |269| `adjustScreenView(page, opts?)` | Fit view + zoom out | After adding components to canvas |270| `zoomOut(page, times)` | Zoom out N times | When components are too small |271| `selectGptModel(page)` | Selects gpt-4o-mini for all Language Model nodes | GPT-dependent tests |272| `addOpenAiInputKey(page)` | Fills OPENAI_API_KEY for all openai_api_key fields | Tests requiring API key |273| `enableInspectPanel(page)` | Toggles inspection panel ON | **MUST call before `edit-fields-button`** |274| `disableInspectPanel(page)` | Toggles inspection panel OFF | Cleanup after inspection |275| `updateOldComponents(page)` | Clicks "Update all" if outdated components exist | After loading saved flows |276| `unselectNodes(page)` | Clicks empty canvas area to deselect all nodes | After node operations |277| `renameFlow(page, { flowName })` | Renames the current flow | Flow management tests |278| `uploadFile(page, filename)` | Uploads a file from test assets | File upload tests |279| `withEventDeliveryModes(...)` | Runs test 3x: streaming, polling, direct | Starter project tests |280 281### initialGPTsetup Options282 283```typescript284await initialGPTsetup(page); // All steps285 286await initialGPTsetup(page, {287 skipAdjustScreenView: true,288 skipUpdateOldComponents: true,289 skipSelectGptModel: true,290});291```292 293### Inspection Panel Pattern (CRITICAL)294 295```typescript296// MUST enable inspection panel FIRST297await enableInspectPanel(page);298 299// Click a node to select it300await page.getByTestId("title-OpenAI").click();301 302// Open field editor303await page.getByTestId("edit-fields-button").click();304 305// Toggle field visibility306await page.getByTestId("showmodel_name").click();307 308// Close field editor309await page.getByTestId("edit-fields-button").click();310```311 312**If you skip `enableInspectPanel(page)`, the `edit-fields-button` will NOT be visible.**313 314## Skip Patterns315 316```typescript317// Skip test if env var missing318test.skip(!process?.env?.OPENAI_API_KEY, "OPENAI_API_KEY required to run this test");319 320// Skip test unconditionally with reason321test.skip(true, "Feature not yet implemented with new designs");322```323 324## Writing Good E2E Tests325 326### Do:327- **Tag every test** with `@release` (plus any domain tags that apply)328- **Import from `../../fixtures`**, not `@playwright/test`329- **Start with `awaitBootstrapTest(page)`** — always330- **Use `getByTestId`** for stable selectors331- **Set explicit timeouts** on `waitForSelector` and `expect(...).toBeVisible()` for async operations332- **Test the complete user flow**: setup → action → verification333- **Use `withEventDeliveryModes`** for tests that involve flow execution (chat, build)334 335### Don't:336- **Don't use `page.waitForTimeout()`** unless absolutely necessary — prefer `waitForSelector` or `expect().toBeVisible()`337- **Don't hardcode API keys** — read from `process.env.OPENAI_API_KEY`338- **Don't skip tests without a reason** — always provide the second argument to `test.skip()`339- **Don't import from `@playwright/test`** — use the custom fixtures340- **Don't forget `enableInspectPanel(page)`** before accessing `edit-fields-button`341- **Don't assume input fields exist** when global variables are selected (badge renders instead)342 343### Challenge Tests (Apply Here Too)344 345E2E tests should also cover adversarial scenarios:346- **Invalid input**: paste 10K characters, special characters (`<script>alert(1)</script>`), empty submissions347- **Network interruption**: what happens if the user loses connection mid-build?348- **Permission boundaries**: can a user access another user's flow via direct URL?349- **Concurrent actions**: double-click delete, rapid chat messages350- **Error recovery**: does the UI recover gracefully from a 500 error?351 352## References353 354- [Selector Patterns](references/selectors.md) — Complete data-testid catalog355- [Helper Functions](references/helpers.md) — Detailed documentation of all 37+ utility functions356- [Test Fixtures](references/fixtures.md) — Custom fixture error detection behavior357 Discovery context
Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.
