前端测试
- 作者仓库星标 42,796
- 作者更新于 实时读取
- 作者仓库 gradio
- 领域
- AI 智能
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 88 / 100 · 社区维护
- 作者 / 版本 / 许可
- @gradio-app · 未声明 license
- Token 消耗评级
- 低消耗
- 接入复杂程度
- 需简单配置
- 是否需要外部 API Key
- 不需要
- 兼容的系统
- 未声明(默认跨平台)
- 底层运行要求
- Python
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- 网络行为
- 仅限本地
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: frontend-unit-testing
description: Write comprehensive, behaviour-driven unit tests for Gradio frontend Svelte components using Vit…
category: AI 智能
runtime: Python
---
# frontend-unit-testing 输出预览
## PART A: 任务判断
- 适用问题:提示词、Agent 工作流、模型评估或自动化推理。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Core Principles (Non-Negotiable) / Test Environment & Utilities / render(Component, props?, options?)”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于提示词、Agent 工作流、模型评估或自动化推理,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Core Principles (Non-Negotiable) / Test Environment & Utilities / render(Component, props?, options?)”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、主要在本地完成、通常不需要额外 API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件;主要在本地完成;通常不需要额外 API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文没有稳定的斜杠命令要求。安装验证后通常全局生效,直接在对话里点名这个 Skill 并描述任务即可。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件。
先用一个小任务确认它会围绕“Core Principles (Non-Negotiable) / Test Environment & Utilities / render(Component, props?, options?)”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: frontend-unit-testing
description: Write comprehensive, behaviour-driven unit tests for Gradio frontend Svelte components using Vit…
category: AI 智能
source: gradio-app/gradio
---
# frontend-unit-testing
## 什么时候使用
- 用于组织测试、定位失败并形成修复闭环 适合处理AI Agent、提示词、模型评估与自动化推理,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的步骤;通常不需要额外…
- 面向提示词、Agent 工作流、模型评估或自动化推理,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Core Principles (Non-Negotiable) / Test Environment & Utilities / render(Component, props?, options?)」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件;主要在本地完成;通常不需要额外 API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "frontend-unit-testing" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Core Principles (Non-Negotiable) / Test Environment & Utilities / render(Component, props?, options?)
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Python | 读取文件、写入/修改文件 | 主要在本地完成
安全层 -> 通常不需要额外 API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} Frontend Unit Testing Skill
You are an expert at writing unit tests for Gradio's Svelte frontend components. Follow these instructions precisely.
Core Principles (Non-Negotiable)
Test everything. Unit tests are cheap. Having too many is a problem we want to have. When in doubt, write the test. Multiple tests per feature/argument is fine and encouraged.
Test behaviour, not implementation. Never assert on implementation details like CSS class names, internal state, or DOM structure for its own sake. Instead, test observable behaviour.
- BAD: assert that an input has a
stepattribute set to5 - GOOD: type a value, click the increment button, and assert the value increased by
5 - BAD: assert that a container has class
hidden - GOOD: assert that the element is not visible with
toBeVisible()
- BAD: assert that an input has a
Test Gradio-specific functionality. Every component has
get_data,set_data, and dispatches events. These must be tested, including their interactions with props.set_data-> verify the DOM reflects the changeget_data-> verify it returns the current stateset_data->get_dataround-trips- User interaction ->
get_datareflects it - Events:
change,input,submit,blur,focus,clear,upload,select,custom_button_click, etc.
Real browser environment. Tests run in Vitest browser mode with a Playwright provider. This is a real browser, not jsdom. Do not mock or stub unless absolutely unavoidable (e.g.,
navigator.clipboard,MediaStream). If you must mock, explain why in a comment.Test sub-components in isolation when they have meaningful standalone logic (e.g., a utility function, a shared inner component). These tests are in addition to full
Index.svelteintegration tests.Never refactor production code for testability without explicit user approval. If a refactor would help, recommend it and wait for a go-ahead.
Visual-only props get
test.todoplaceholders. If a prop or argument results in a purely visual change (colours, spacing, fonts, border styles, shadows, etc.) that cannot be meaningfully asserted with behavioural queries, do NOT skip it silently. Instead:- Add a
test.todo("description")explaining that it needs a visual regression test - The description should state what prop/value is being tested and what the expected visual outcome is
- This ensures visual-only behaviour is tracked and not forgotten
test.todo( "VISUAL: container_color='red' applies a red background to the component wrapper — needs Playwright visual regression screenshot comparison" );- Add a
No useless comments. Comments should be used exceptionally, only when clarification of the code is essential. Do NOT create comments to describe types of tests (
describeblocks do that). Do NOT add comments explaining the flow of the code (the code does that). Only add comments when something is confusing or complex, adds useful context (i.e. giving more detail on the failure case it is guarding against), or goes against our principles (this requires a comment + rationale).
Test Environment & Utilities
All test utilities come from @self/tootils/render. Never import from @testing-library/svelte directly.
render(Component, props?, options?)
Mounts a Gradio component with the full shared prop infrastructure (loading_status, dispatcher, i18n, etc.).
Returns:
container— the root DOM elementlisten(event_name, opts?)— returns avi.fn()mock that records dispatched Gradio events. By default only captures events fired after the call. Use{ retrospective: true }when testing mount-time events — this replays any events that were buffered during render beforelistenwas called. Without this flag, mount-time events are invisible.set_data(data)— programmatically set component data (simulates backend push). Automatically ticks.get_data()— read current component data.- All
@testing-library/domquery functions (getByRole,getByText,getByDisplayValue,queryByRole, etc.) debug()— prints pretty DOM to console.unmount()— teardown.
Props are split automatically: keys in allowed_shared_props go to shared_props, everything else goes to props.
fireEvent
Re-exported from @testing-library/dom but wrapped to await tick() twice after each event (to let Svelte reactivity settle). Always await fireEvent calls.
cleanup()
Call in afterEach to unmount all rendered components.
run_shared_prop_tests(config) (MANDATORY)
Runs a standard suite of shared prop tests (elem_id, elem_classes, visible, label, show_label, validation_error). Every component test file MUST call this. Never manually re-implement these tests.
run_shared_prop_tests({
component: MyComponent,
name: "MyComponent",
base_props: { /* minimum props to render */ },
has_label: true, // default true; false for label-less components
has_validation_error: true // default true
});
When a shared test doesn't apply to a component (e.g., the component has no label), use the config flags to disable that specific test — do NOT skip run_shared_prop_tests entirely and rewrite everything by hand:
// Accordion has no label — disable label tests, keep everything else
run_shared_prop_tests({
component: Accordion,
name: "Accordion",
base_props: { label: "Section", open: true },
has_label: false,
has_validation_error: false
});
If a shared test fails for a component-specific reason that the flags don't cover, the correct response is to:
- Still call
run_shared_prop_testswith appropriate flags to cover what it can - Write a targeted custom test for the specific behaviour that differs
- Explain why the shared test doesn't apply in a comment
File utilities
upload_file(fixture, selector?)— sets files on a file input using real fixturesdrop_file(fixture, selector)— simulates drag-and-drop with real filesdownload_file(selector)— clicks an element and captures the downloadmock_client()— returns a mock client for components that use file uploads (the upload mock echoes input unchanged)
Fixtures
Pre-built FileData objects pointing to real test files:
TEST_TXT,TEST_JPG,TEST_PNG,TEST_MP4,TEST_WAV,TEST_PDF
User events
For keyboard/typing interactions, import @testing-library/user-event:
import event from "@testing-library/user-event";
el.focus();
await event.keyboard("some text");
await event.type(el, "123");
await event.clear(el);
Running Tests
Always use pnpm test:run. Never use pnpm test — it starts in watch mode and never exits.
All commands are run from the repo root.
# Run all unit tests
pnpm test:run
# Run a specific test file (match by filename)
pnpm test:run Textbox.test.ts
# Run all tests within a folder (match by path segment)
pnpm test:run dataframe
# Filter by test name with -t
pnpm test:run -t elem_id
# Combine file/folder filter with test name filter
pnpm test:run accordion -t elem_id
After writing or modifying tests, always run them to verify they pass.
Test File Structure
import { test, describe, afterEach, expect, vi } from "vitest";
import { cleanup, render, fireEvent, waitFor } from "@self/tootils/render";
import { run_shared_prop_tests } from "@self/tootils/shared-prop-tests";
import event from "@testing-library/user-event";
import Component from "./Index.svelte";
const default_props = {
// Minimum props for a working render, always including:
label: "Component Name",
show_label: true,
interactive: true,
// ...component-specific props
};
// 1. Shared prop tests
run_shared_prop_tests({
component: Component,
name: "ComponentName",
base_props: { /* ... */ }
});
// 2. Describe blocks grouped by prop, feature, or concern
describe("ComponentName", () => {
afterEach(() => cleanup());
// General rendering and basic behaviour
});
describe("Props: propName", () => {
afterEach(() => cleanup());
// Tests for each meaningful prop value
});
describe("Events", () => {
afterEach(() => cleanup());
// change, input, submit, blur, focus, clear, etc.
});
describe("get_data / set_data", () => {
afterEach(() => cleanup());
// Round-trip, DOM reflection, interaction flow
});
describe("Edge cases", () => {
afterEach(() => cleanup());
// Null/undefined handling, deduplication, mount-time behaviour
});
Naming conventions
- Describe blocks: Group by
Props: <name>,Events,Events: <name>,get_data / set_data,Edge cases, or component area. - Test names: Declarative sentences describing what should happen. e.g.,
"lines > 1 renders a textarea with correct rows","change: emitted when value changes from outside".
Two Modes of Operation
Mode 1: Targeted tests (for a specific feature or regression)
When asked to write tests for a specific feature, prop, or bug fix:
- Read the relevant component source to understand the behaviour
- Write focused tests covering the specific area
- Include edge cases related to that feature
- Proceed directly — no plan needed unless the scope is ambiguous
Mode 2: Full component test battery
When asked to write or rewrite tests for an entire component:
You MUST follow this process:
Research phase — Read thoroughly:
- The component's
Index.svelte(the main entry point) - Any shared sub-components in the component's
shared/directory - The Python component definition in
gradio/components/to understand all props, events, and data types - Any existing tests for the component
- The component's demo files in
demo/if they exist
- The component's
Analysis phase — Identify every testable surface:
- Every prop and its meaningful values (including defaults, edge cases, combinations)
- Every event the component dispatches
get_data/set_databehaviour- Interactive vs non-interactive behaviour
- Sub-components with standalone testable logic
- Accessibility-relevant behaviour (labels, ARIA attributes as they affect user behaviour)
- Edge cases: null/undefined values, empty strings, boundary values, mount-time behaviour, event deduplication
- Visual-only props: identify props that only affect appearance and flag them for
test.todowith visual regression notes
Plan phase — Present a structured testing plan:
- Organised by describe block
- Each test listed with: name, what it verifies, key assertion
- Call out any tests that might need mocking (and why)
- Call out any sub-components worth testing in isolation
- Call out any visual-only props that need
test.todoplaceholders for visual regression testing - Note any refactoring that would improve testability (but don't do it)
Wait for approval — Present the plan and ask for feedback before writing code.
Implementation phase — Write the tests following the plan.
Assertion Patterns
Query priority (strict)
Always use the query utilities returned by render(). Never use container.querySelector unless every option below has been exhausted. Follow this priority order:
Semantic role queries (best — reflects how users and assistive tech see the component):
getByRole("textbox") getByRole("button", { name: "Submit" }) getByRole("slider")Label and text queries (good — reflects visible content):
getByLabelText("Upload file") getByText("Submit") getByDisplayValue("hello") getByPlaceholderText("Enter text...")Test ID queries (required fallback — when no semantic/text query works):
getByTestId("source-select") getByTestId("password")If the element lacks a
data-testid, add one to the component source. This is always the right move.container.querySelectoris never acceptable — adding adata-testidis cheap, explicit, and keeps test intent clear.
Use queryBy* variants (which return null instead of throwing) when asserting something is not in the DOM:
expect(queryByRole("button")).not.toBeInTheDocument();
expect(queryByLabelText("Upload file")).not.toBeInTheDocument();
Common assertion patterns
// Visibility
expect(el).toBeVisible();
expect(el).not.toBeVisible();
// Presence
expect(queryByRole("button")).not.toBeInTheDocument(); // not in DOM
expect(getByRole("button")).toBeInTheDocument(); // in DOM
// Values
expect(el).toHaveValue("hello");
expect(el).toHaveAttribute("type", "password");
// State
expect(el).toBeDisabled();
expect(el).toBeEnabled();
expect(el).toHaveFocus();
// Events
const change = listen("change");
await set_data({ value: "new" });
expect(change).toHaveBeenCalledTimes(1);
expect(change).toHaveBeenCalledWith("new");
// Mount-time events — use { retrospective: true }
// listen() only captures events fired AFTER it is called. Since render()
// is awaited before listen() runs, any events fired during mount are missed.
// Pass { retrospective: true } to also replay events from the buffer.
//
// Use this whenever you need to assert about mount-time behaviour:
// - "no spurious change event on mount"
// - "component fires 'load' on mount"
// - "initial value triggers change on mount" (or doesn't)
const change = listen("change", { retrospective: true });
expect(change).not.toHaveBeenCalled(); // no spurious mount event
// If you expect an event WAS fired on mount:
const load = listen("load", { retrospective: true });
expect(load).toHaveBeenCalledTimes(1);
// Event deduplication
await set_data({ value: "x" });
await set_data({ value: "x" });
expect(change).toHaveBeenCalledTimes(1);
// Async operations (uploads, etc.)
await waitFor(() => {
expect(upload).toHaveBeenCalledTimes(1);
});
What NOT to Do
- Never use
container.querySelector. It is unconditionally banned. UsegetByRole,getByText,getByLabelText,getByDisplayValue,getByPlaceholderText, orgetByTestId. If none of those work, add adata-testidattribute to the component source — this is always the correct solution. - Don't mock Svelte internals, the DOM, or browser APIs that work natively.
- Don't unit-test purely visual styling (colours, spacing, fonts, shadows). Instead, add
test.todoplaceholders recommending Playwright visual regression tests. Do test behavioural effects of styling (visibility, disabled state). - Don't assert on internal class names unless they are the mechanism by which a behaviour is expressed and there's no semantic alternative (e.g.,
.sr-onlyfor screen-reader-only labels). - Don't manually rewrite shared prop tests.
run_shared_prop_testshandleselem_id,elem_classes,visible,label,show_label,validation_error. Always call it. If a specific test doesn't apply, use the config flags (has_label: false,has_validation_error: false) — never skip the utility and hand-roll the tests instead. - Don't use
toBeTruthy()ortoBeFalsy(). These are too vague and hide intent. Use the most specific matcher for the value being checked:- Element in DOM →
toBeInTheDocument()/.not.toBeInTheDocument() - Element visible →
toBeVisible()/.not.toBeVisible() - Element has value →
toHaveValue("x") - Element checked →
toBeChecked() - Element disabled →
toBeDisabled()/toBeEnabled() - Boolean variable →
toBe(true)/toBe(false) - Array non-empty →
toHaveLength(n)orexpect(arr.length).toBeGreaterThan(0) - Non-DOM null/undefined →
toBeNull()/toBeDefined()/toBeUndefined()
- Element in DOM →
- Don't add
setTimeoutor artificial delays. Useawait tick(),await fireEvent.x(), orawait waitFor(). - Don't write snapshot tests. They test implementation, not behaviour.
- Don't import from
@testing-library/svelte— always use@self/tootils/render.
Reference: Exemplar Test Files
Study these files for patterns and quality bar:
js/textbox/Textbox.test.ts— comprehensive prop, event, and edge case testingjs/image/Image.test.ts— file upload/drop, sub-component isolation (get_coordinates_of_clicked_image), interactive vs static modes, custom buttons
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核