mcpField
E
e2e-testing

e2e-testing

github.com/langflow-ai测试与质量156.1k39 次安装6 天前更新
代码仓库

关于 e2e-testing

为 Langflow 前端提供 Playwright 端到端测试编写与维护指南,面向测试新功能、排查失败用例和审查覆盖率的开发者。内容包括自定义夹具、选择器、辅助函数与标签规范。

主要功能

  1. 01编写和筛选 Playwright E2E 测试
  2. 02使用夹具检测 API 与流程错误
  3. 03按优先级选择稳定的元素定位方式
  4. 04复用启动、视图和流程辅助函数
  5. 05为测试添加发布与领域标签

适用场景

  1. 01为新功能编写 E2E 测试
  2. 02排查失败的 Playwright 测试
  3. 03审查测试标签与覆盖范围

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/langflow-ai/langflow --skill e2e-testing

安装后在 Claude Code 中输入/e2e-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
fixtures.md
helpers.md
selectors.md

详细介绍

Langflow E2E 测试(Playwright)

适用场景

  • 用户要求为某个功能或流程编写 E2E 测试
  • 用户要求修复失败的 E2E 测试
  • 用户要求审查 E2E 测试覆盖率
  • 用户修改组件中的 data-testid 属性(可能会导致现有测试失败)
  • 用户修改 src/frontend/tests/utils/ 中的测试工具

以下情况不要应用:

  • 用户询问单元测试(使用 frontend-testing 技能进行 Jest 测试)
  • 用户询问后端测试(使用 backend-code-review 技能进行 pytest 测试)

技术栈

工具版本用途
Playwright1.59.1E2E 测试运行器 + 浏览器自动化
Chromium(捆绑)默认浏览器(已禁用 Firefox/Safari)
自定义 fixturetests/fixtures.ts自动检测 API 错误和流程执行失败

关键命令

# Run all E2E tests
npx playwright test

# Run tests filtered by tag
npx playwright test --grep "@release"
npx playwright test --grep "@workspace"
npx playwright test --grep "@starter-projects"

# Run a specific test file
npx playwright test tests/core/features/run-flow.spec.ts

# Debug mode (headed browser + step through)
npx playwright test --debug

# Show HTML report after run
npx playwright show-report

# Update snapshots (if used)
npx playwright test --update-snapshots

配置

文件:src/frontend/playwright.config.ts

设置值原因
fullyParalleltrue并行运行测试以提升速度
timeout5 分钟流程构建可能较慢;防止误报超时
retries3(本地),2(CI)网络/渲染问题可能不稳定;重试可捕获这些问题
workers2平衡速度和资源使用
actionTimeout20s单项操作超时(点击、填充等)
traceon-first-retry失败时捕获跟踪信息以便调试
baseURLhttp://localhost:3000Vite 开发服务器

WebServer:Playwright 会自动启动后端(7860 上的 uvicorn)和前端(3000 上的 npm start)。

目录结构

src/frontend/tests/
├── fixtures.ts                     # Custom test fixture with error detection
├── globalTeardown.ts               # Cleanup (removes temp DB after tests)
├── core/
│   ├── features/                   # Main feature tests (run-flow, playground, etc.)
│   ├── integrations/               # Starter project / template tests
│   ├── regression/                 # Bug regression tests
│   └── unit/                       # Component-level Playwright tests
├── extended/
│   ├── features/                   # Extended features (MCP, auto-save, etc.)
│   ├── integrations/               # Extended integrations
│   └── regression/                 # Extended regressions
└── utils/                          # 37+ shared helper functions

文件命名

  • 使用 kebab-case 并以 .spec.ts 为后缀:run-flow.spec.ts、playground.spec.ts、flow-lock.spec.ts
  • 模板测试可以使用空格:Document QA.spec.ts、Social Media Agent.spec.ts
  • 用于并行执行的分片测试:chatInputOutputUser-shard-0.spec.ts

注意:E2E 测试使用 .spec.ts(Playwright 约定)。单元测试使用 .test.tsx(Jest 约定)。不要混用。

测试结构

基本测试
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";

test(
  "user should be able to run a flow successfully",
  { tag: ["@release", "@workspace"] },
  async ({ page }) => {
    await awaitBootstrapTest(page);

    // Arrange: Create a flow
    await page.getByTestId("blank-flow").click();

    // Act: Add components and run
    await page.getByTestId("sidebar-search-input").fill("Chat Output");
    // ... setup ...

    // Assert: Verify result
    await expect(page.getByTestId("build-status-success")).toBeVisible({ timeout: 30000 });
  },
);
使用 test.describe
test.describe("Flow Lock Feature", () => {
  test(
    "should lock and unlock a flow",
    { tag: ["@release", "@api"] },
    async ({ page }) => {
      // ...
    },
  );

  test(
    "should prevent editing when locked",
    { tag: ["@release"] },
    async ({ page }) => {
      // ...
    },
  );
});
使用串行模式(测试存在顺序依赖)
test.describe.configure({ mode: "serial" });

test("step 1: create flow", async ({ page }) => { /* ... */ });
test("step 2: edit flow", async ({ page }) => { /* ... */ });
test("step 3: delete flow", async ({ page }) => { /* ... */ });
使用事件传递模式(流式/轮询/直接)
import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";

withEventDeliveryModes(
  "Document Q&A should work",
  { tag: ["@release", "@starter-projects"] },
  async ({ page }) => {
    // This test runs 3 times: streaming, polling, direct
    // Each mode is configured automatically via route interception
  },
);

标签系统

每个测试都必须标记为 @release——发布运行会搜索此标签,因此未标记或标签错误的测试规范会悄无声息地从发布覆盖范围中移除。在 @release 之外,还要添加下表中的领域标签(一个测试可以有多个标签)。以下六个是唯一允许的标签;不要自行创建新标签。

标签用途使用时机
@release属于发布运行(每个测试规范都必须添加)所有测试
@workspace工作区/流程管理创建、编辑、删除流程
@api依赖 API 的功能调用后端端点的测试
@database数据库操作涉及持久化的测试
@components组件级测试单个组件的行为
@starter-projects模板/起始项目测试预构建的流程模板
// Right: tag your test
test("my feature test", { tag: ["@release", "@workspace"] }, async ({ page }) => { ... });

// Wrong: no tags — test can't be filtered
test("my feature test", async ({ page }) => { ... });

自定义 fixture:错误检测

始终从 ../../fixtures 导入 test 和 expect,不要从 @playwright/test 导入。

// Right
import { expect, test } from "../../fixtures";

// Wrong — bypasses error detection
import { expect, test } from "@playwright/test";

原因:自定义 fixture 会自动监控所有 /api/ 响应,并在以下情况发生时使测试失败:

  • 出现 HTTP 400、404、422 或 500 错误
  • 流程执行在事件流中返回 error: true
  • 流式响应中出现 Python 异常

若要选择允许预期错误(例如,测试错误处理):

test("should show error on invalid input", { tag: ["@release"] }, async ({ page }) => {
  page.allowFlowErrors();  // Allow flow errors for this test
  // ... test that expects errors ...
});

选择器策略

优先级(按优先顺序)
  1. getByTestId——最稳定,在 Langflow 中约 95% 的情况下都会使用
  2. getByRole——用于按钮、标题和表单元素
  3. getByText——用于可见文本内容
  4. waitForSelector——用于 CSS 选择器和动态元素
  5. locator——用于复杂选择器(CSS、XPath)
常见的 data-testid 模式

画布与导航:

  • blank-flow — 新建空白流程按钮
  • sidebar-search-input — 组件搜索
  • canvas_controls_dropdown — 画布控制菜单
  • fit_view、zoom_out、zoom_in — 画布控制
  • react-flow-id — ReactFlow 画布容器

组件字段:

  • popover-anchor-input-{fieldname} — 组件参数的输入字段
  • input-chat-playground — Playground 聊天输入框
  • div-chat-message — Playground 中的聊天消息

操作:

  • add-component-button-{component} — 将组件添加到画布
  • button-send — 发送聊天消息
  • button_run_{component} — 运行指定组件
  • publish-button、save-flow-button — 流程操作
  • edit-fields-button — 切换检查面板字段编辑器

模态框与面板:

  • modal-title — 模态框标题
  • icon-Globe — 全局变量
  • icon-Lock — 流程锁定切换
  • session-selector — Playground 会话切换器
重要:全局变量与徽标

当组件字段选中了全局变量(load_from_db: true + value: "OPENAI_API_KEY")时,该字段会渲染一个徽标,而不是 <input> 元素。这意味着 getByTestId("popover-anchor-input-api_key") 找不到该元素——它不存在于 DOM 中。

预先选中了全局变量的模板:Market Research、Price Deal Finder、Research Agent。 未预先选中全局变量的模板(会渲染输入框):Instagram Copywriter。

核心辅助函数

位于 src/frontend/tests/utils/:

函数功能使用时机
awaitBootstrapTest(page)等待应用完全加载每个测试的开头
initialGPTsetup(page)完整设置:adjustView → updateComponents → selectModel → addKey → adjustView → unselectNodes需要配置 OpenAI 的测试
adjustScreenView(page, opts?)适应视图 + 缩小将组件添加到画布后
zoomOut(page, times)缩小 N 次组件太小时
selectGptModel(page)为所有 Language Model 节点选择 gpt-4o-mini依赖 GPT 的测试
addOpenAiInputKey(page)为所有 openai_api_key 字段填入 OPENAI_API_KEY需要 API 密钥的测试
enableInspectPanel(page)开启检查面板必须在调用 edit-fields-button 前调用
disableInspectPanel(page)关闭检查面板检查完成后的清理
updateOldComponents(page)如果存在过时组件,则点击 "Update all"加载已保存的流程后
unselectNodes(page)点击画布空白区域以取消选中所有节点节点操作后
renameFlow(page, { flowName })重命名当前流程流程管理测试
uploadFile(page, filename)从测试资源上传文件文件上传测试
withEventDeliveryModes(...)运行测试 3 次:streaming、polling、directStarter project 测试
initialGPTsetup 选项
await initialGPTsetup(page);  // All steps

await initialGPTsetup(page, {
  skipAdjustScreenView: true,
  skipUpdateOldComponents: true,
  skipSelectGptModel: true,
});
检查面板模式(至关重要)
// MUST enable inspection panel FIRST
await enableInspectPanel(page);

// Click a node to select it
await page.getByTestId("title-OpenAI").click();

// Open field editor
await page.getByTestId("edit-fields-button").click();

// Toggle field visibility
await page.getByTestId("showmodel_name").click();

// Close field editor
await page.getByTestId("edit-fields-button").click();

如果跳过 enableInspectPanel(page),edit-fields-button 将不会显示。

跳过模式

// Skip test if env var missing
test.skip(!process?.env?.OPENAI_API_KEY, "OPENAI_API_KEY required to run this test");

// Skip test unconditionally with reason
test.skip(true, "Feature not yet implemented with new designs");

编写优秀的端到端测试

应该:
  • 为每个测试添加 @release 标签(以及适用的任何领域标签)
  • 从 ../../fixtures 导入,而不是从 @playwright/test 导入
  • 以 awaitBootstrapTest(page) 开始——务必如此
  • 使用 getByTestId 作为稳定的选择器
  • 对异步操作的 waitForSelector 和 expect(...).toBeVisible() 设置明确的超时时间
  • 测试完整的用户流程:设置 → 操作 → 验证
  • 对涉及流程执行(chat、build)的测试,使用 withEventDeliveryModes
不要:
  • 不要使用 page.waitForTimeout(),除非绝对必要——优先使用 waitForSelector 或 expect().toBeVisible()
  • 不要硬编码 API 密钥——从 process.env.OPENAI_API_KEY 读取
  • 不要无故跳过测试——始终为 test.skip() 提供第二个参数
  • 不要从 @playwright/test 导入——使用自定义夹具
  • 在访问 edit-fields-button 前,别忘了调用 enableInspectPanel(page)
  • 选择全局变量时,不要假设输入字段存在(此时会渲染徽标)
挑战测试(此处也适用)

E2E 测试还应覆盖对抗性场景:

  • 无效输入:粘贴 10K 个字符、特殊字符(<script>alert(1)</script>)、空提交
  • 网络中断:如果用户在构建过程中失去连接,会发生什么?
  • 权限边界:用户能否通过直接 URL 访问其他用户的流程?
  • 并发操作:双击删除、快速发送聊天消息
  • 错误恢复:UI 是否能从 500 错误中顺利恢复?

参考资料

评论

支持 Markdown