一键导入
vitest-testing-guidelines
Use when adding or changing tests in this Vitest-based monorepo.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when adding or changing tests in this Vitest-based monorepo.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when adding, changing, extracting, or reusing client components, including Storybook stories and controls.
Use when adding or changing forms in apps/client.
Use when changing GitHub OAuth, session cookies, SessionGuard, logout, or authenticated API request identity.
Use when adding or changing routes in apps/client.
Use when adding or changing client state, React Query data flow, immutable context values, or Zustand stores.
Use when styling apps/client with Tailwind CSS v4.
| name | vitest-testing-guidelines |
| description | Use when adding or changing tests in this Vitest-based monorepo. |
Generated from ai/registry.json. Do not edit manually.
Canonical skill: ../../../ai/skills/vitest-testing-guidelines.md.
Referenced context:
../../../ai/rules/testing-rules.md../../../ai/architecture/monorepo.md../../../ai/architecture/client-app.md../../../ai/examples/good-evaluator-test.md../../../ai/examples/good-public-config-service.mdThis file is compiled from canonical AI knowledge files. Edit canonical files under ai, then run npm run ai:sync.
ai/skills/vitest-testing-guidelines.mdUse this skill when adding or changing tests in this Vitest-based monorepo.
Add focused tests that protect observable behavior and domain invariants without coupling to implementation details.
ai/rules/testing-rules.mdai/architecture/monorepo.mdai/architecture/client-app.mdai/examples/good-evaluator-test.mdai/examples/good-public-config-service.mdapps/client, place tests in the owning __tests__/ folder described by the client architecture and use existing Testing Library/React Query test helpers.apps/client API operations and hooks, test both successful mocked responses and mocked API error responses.__tests__/ folder layout instead of loose sibling *.test.ts(x) files..only remains.npm --workspace @capture-flag/client run test:coverage after changing client coverage configuration or broad client tests.ai/rules/testing-rules.mdTesting rules for this Vitest-based monorepo.
describe, it, expect, vi.fn, and vi.spyOn.__tests__/ folders next to the source area they cover, matching the Storybook stories/ grouping convention.apps/client/src/api request and hook tests in src/api/__tests__, using the existing fetch, React Query, and render helpers from src/test.apps/client/src/core tests under src/core/<category>/__tests__/<name>.test.ts.src/components/__tests__ and shared member component tests under src/components/members/__tests__.src/pages/<PageName>/__tests__ or src/pages/<PageName>/<section>/__tests__ for colocated page internals.src/__tests__ only for source files owned directly by apps/client/src, such as permissions.ts.apps/client code that calls the API, cover both successful responses and API error responses with mocked fetch/response behavior.apps/client coverage at 90% or higher for configured client coverage targets; aim for 100% on pure helpers and request functions.vi.fn() methods.jest.mock patterns..only in tests.apps/client tests beside source files as loose *.test.ts(x) files when a nearby __tests__/ folder is available.src/test/render.tsx, src/test/api.ts, or src/test/pageApi.ts already cover the setup.npm --workspace @capture-flag/api run test.npm --workspace @capture-flag/client run test.npm --workspace @capture-flag/client run test:coverage.npm --workspace @capture-flag/shared run test.npm --workspace @capture-flag/evaluator run test.npm --workspace @capture-flag/sdk-js run test.npm run test.ai/architecture/monorepo.mdCapture Flag is a TypeScript npm workspaces monorepo.
apps/api: NestJS API, Prisma access, authentication, tenant services, public SDK config endpoint.apps/client: Vite React app for platform UI.packages/shared: shared package boundary for reusable cross-workspace code.packages/evaluator: pure local evaluation engine.packages/sdk-js: JavaScript SDK that fetches public config and evaluates locally.packages/react: React provider and hook around the JavaScript SDK.npm run build.npm run test.npm run lint.npm run ai:sync.npm run ai:check.apps/api.apps/client.packages/* only when there is a real cross-workspace consumer.docs/* are part of the source of truth for behavior.ai/architecture/client-app.mdapps/client is a Vite React application for the Capture Flag platform UI.
src/main.tsx owns top-level providers.src/router.tsx owns React Router route definitions.lazyRoute() helper and should expose named exports.src/layouts contains route layout wrappers that render shared shells, navigation, headers, and nested <Outlet /> regions.src/pages contains route-level screens. Multi-file route screens use folder modules with index.ts named exports; simple one-file screens may stay as direct page files.src/components contains shared UI used by multiple pages or sections.src/core contains context-independent client utilities and reusable hooks organized by category.src/api contains client request functions, React Query hooks, operation barrels, domain barrels, and domain query keys.src/routing contains route path and route context helpers shared by pages and layouts.src/stories contains shared Storybook fixtures and API mocks, not component stories.src/test contains shared Vitest and Testing Library helpers.PlatformLayout owns the authenticated shell, navigation frame, sidebar/header state, logout flow, and nested route outlet.useRouteContext, not stored in a mutable layout context./: redirects to /organizations./login: GitHub login screen./account: authenticated user account details and display name editing./organizations and /organizations/:organizationId: organization selection and organization members./organizations/:organizationId/projects and /organizations/:organizationId/projects/:projectId: project selection and project members./organizations/:organizationId/projects/:projectId/environments: environments for the selected project./organizations/:organizationId/projects/:projectId/configs and /organizations/:organizationId/projects/:projectId/configs/:configId: configs and public Config JSON preview./organizations/:organizationId/projects/:projectId/configs/:configId/flags: feature flags and remote config values./organizations/:organizationId/projects/:projectId/configs/:configId/segments: reusable targeting segments./organizations/:organizationId/projects/:projectId/sdk-keys: SDK key lifecycle for project configs/environments, with selected config/environment in ?configId= and ?environmentId= when needed./organizations/:organizationId/audit-logs: organization/project audit log timeline, with selected project in ?projectId= when needed.*: redirects to /.src/api/<domain>/<operation>.useQuery or useInfiniteQuery according to the API shape.useRouteContext for selected resources and redirect-safe navigation paths.src/layouts/<LayoutName> until reused by another layout or page.src/components and are imported directly through aliases such as @components/Button.components/members primitives with page-specific role options.stories/ child folder beside the source area they cover.src/pages/stories.src/stories and are reused by stories and page tests.src/core/<category>/<name>.ts.date, json, strings, validation, and hooks.src/core/date/toDate.index.ts barrels under src/core; multiple helpers require multiple explicit imports.src/core/<category>/__tests__/<name>.test.ts next to the category they cover.__tests__/ child folders beside the source area they cover, mirroring Storybook stories/ folders.src/test and provide Testing Library render helpers, React Query providers, and fetch response mocks.npm --workspace @capture-flag/client run test:coverage and should stay at 90% or higher for configured client targets.src/api/__tests__ and cover operation behavior through mocked fetch responses.src/components/__tests__; member component tests live in src/components/members/__tests__.src/pages/<PageName>/__tests__ when they cover one page folder.src/pages/<PageName>/<section>/__tests__ when they cover colocated page internals such as feature flags or segments.src/__tests__ only for source files owned directly by src, such as permissions.ts.src/components/stories; member component stories live in src/components/members/stories.src/layouts/<LayoutName>/stories.stories/ child folder.src/pages/stories and use route parameters plus shared mock data to render realistic page states.src/stories; that folder is reserved for shared Storybook data, routes, and API mocks.ai/examples/good-evaluator-test.mdSource: packages/evaluator/test/index.spec.ts (sha256: 40f396c39736d1e39ecc8575adc4f3566a656b3665b3513c10b64d3d49018e81)
Why this is canonical:
Canonical evaluator test pattern from packages/evaluator/test/index.spec.ts.
function createFlag(overrides: Partial<CaptureFlagConfigFlag> = {}): CaptureFlagConfigFlag {
return {
defaultValue: false,
percentageAttribute: "identifier",
percentageOptions: [],
rules: [],
type: "boolean",
...overrides,
};
}
function createConfig(flag: CaptureFlagConfigFlag = createFlag()): CaptureFlagConfig {
return {
configKey: "default",
environment: "production",
flags: {
newCheckout: flag,
},
generatedAt: "2026-05-12T00:00:00.000Z",
projectKey: "ecommerce",
revision: 1,
schemaVersion: 1,
};
}
Tests build small config fixtures and assert behavior through the public evaluate function.
ai/examples/good-public-config-service.mdSource: apps/api/src/public-sdk/public-sdk.service.ts (sha256: 85e49bab9d48419a9c4333b902d45cd724ae62a4b946eea88831bc3a4d42007c)
Why this is canonical:
Canonical public config pattern from apps/api/src/public-sdk/public-sdk.service.ts.
const transactionResult = await this.prisma.$transaction(
async (tx) => {
const sdkKey = await this.sdkKeyAuth.findActiveSdkKey(tx, rawSdkKey);
const state = await tx.configEnvironmentState.findUnique({
where: {
configId_environmentId: {
configId: sdkKey.configId,
environmentId: sdkKey.environmentId,
},
},
});
},
{ isolationLevel: Prisma.TransactionIsolationLevel.RepeatableRead },
);
The public endpoint authenticates by hashed SDK key and reads config state in the same transaction as flag values.
if (this.cache.matchesIfNoneMatch(ifNoneMatch, state.etag)) {
return {
result: {
etag: state.etag,
cacheControl,
notModified: true,
},
sdkKeyId: sdkKey.id,
};
}
Not-modified responses still count as valid SDK config access for lastUsedAt.
const cacheControl = this.cache.cacheControlHeader();
await this.sdkKeyUsage.recordUse(transactionResult.sdkKeyId);
The public endpoint defaults away from shared caching because the raw SDK key is in the URL path, and usage telemetry is best-effort after a valid config read.