@cosmicdrift/kumiko-testing

中文文档
0.419.0发布于 2026/11/11MITTypeScriptES Module构建来源已验证近 30 天下载 2.1万

Test template for Kumiko apps: per-flow seedTenant, app test stack, canonical test preloads, bunfig generator and integration runner.

原始使用文档

@cosmicdrift/kumiko-testing

Test template for Kumiko apps. Test-only: never import it from production code.

import { seedTenant, setupAppTestStack } from "@cosmicdrift/kumiko-dev-server/testing";

const stack = await setupAppTestStack([myFeature]);
const tenant = await seedTenant(stack, { users: 2 }); // light: no rows, own id/key/users
const persisted = await seedTenant(stack, { persist: true }); // real rows via the dispatcher
await tenant.api.writeOk("my-feature:write:thing:create", { title: "x" });
  • preload/{temporal,ci-log,scrub-env,schema-env-defaults,env,real,dom}: bunfig preload entries. scrub-env removes PROVIDER_ENV_KEYS (unit default); schema-env-defaults fills JWT_SECRET and the KUMIKO_SECRETS_MASTER_KEY_* pair that the framework's own env schema requires (unit, DOM, integration, real — never a service endpoint); env adds schema-env-defaults plus the localhost service defaults (integration); real refuses to run without KUMIKO_REAL_PROVIDERS=1 and in CI, and keeps the keys; dom registers happy-dom (window/document/HTMLElement) while preserving Bun's native fetch/Request/Response, and cleans up (@testing-library/react unmount, Radix DOM/style leaks, window.location) after every test.
  • kumiko-testing bunfig [--dom] [--coverage]: writes bunfig.toml, bunfig.integration.toml and bunfig.real.toml (plus bunfig.dom.toml). --dom adds preload/dom (happy-dom + testing-library/react cleanup); @happy-dom/global-registrator and @testing-library/react (which needs @testing-library/dom) are optional peer dependencies, install them when you use --dom.
  • kumiko-testing integration [--parallel N] [--timings <file>]: runs *.integration.test.ts with the 15s budget. --parallel only when you ask for it, and then with --no-isolate: bun 1.4.0's implicit --isolate leaks native memory per file until CI workers are OOM-killed.

E2E (./e2e, ./e2e/seed-route)

@playwright/test is an optional peer dependency; only the ./e2e subpath imports it. The server side (./e2e/seed-route) never does.

// e2e/server.ts: mount the seed routes next to the app
runDevApp({ features, auth: { admin }, extraRoutes: createE2eSeedRoutes() });

// playwright.config.ts
export default defineAppE2eConfig({ port: 4321 });

// e2e/flow.spec.ts
import { expect, test } from "@cosmicdrift/kumiko-testing/e2e";
test("member sees the note", async ({ seedTenant, page }) => {
  const tenant = await seedTenant({ users: 1, with: [seedNotes] });
  await tenant.loginAs(page, tenant.members[0]!);
});
  • defineAppE2eConfig({ port, env?, locale?, projects?, serverEntry?, testDir?, testMatch?, isolatedSpecs? }): parallelism, retries, timeouts and workers (KUMIKO_E2E_WORKERS overrides) are fixed by the template; projects cannot set them.
  • isolatedSpecs: ["**/composer.spec.ts"] moves specs that change app-wide state (catalog entries, global settings) into an isolated twin of each project (chromium-isolated, <name>-isolated), which runs after all parallel projects on one worker with the project's own use. A project with its own testMatch is rejected. It cannot be combined with --shard; details in docs/guides/testing-standard.md.
  • KUMIKO_E2E_REMOTE_BASE_URL=https://staging.example.com runs the same specs against an already running instance: no web server is started and baseURL is that URL (http or https only). Add KUMIKO_E2E_STORAGE_STATE=<file> for a session recorded with playwright codegen --save-storage; timeouts, viewport and DPR stay with the template. The seed routes do not exist on such a target, so use it for specs that only need an existing session. Trace, screenshot and video are off there, because a trace zip holds the target's session cookie and data. csrfFetch handles the __Host- prefixed csrf cookie an https target sets.
  • The seed routes answer only with KUMIKO_TEST_SEED=1, outside NODE_ENV=production, and with the per-run KUMIKO_TEST_SEED_TOKEN in the x-kumiko-test-seed header. defineAppE2eConfig sets all three.
  • The template also sets KUMIKO_DEV_STYLESHEET_WATCH=0, so the dev-server builds the app's CSS once and never starts a Tailwind --watch process — that watcher would otherwise treat every Playwright artifact write under test-results/ as a rebuild trigger.
  • Each run gets its own Meilisearch index prefix e2e_<uuid>_ (KUMIKO_MEILI_INDEX_PREFIX, reserved), and the template's globalTeardown drops exactly those indexes. A config with its own teardown keeps the cleanup with globalTeardown: [base.globalTeardown, "./e2e/teardown.ts"]. A killed run leaves its indexes behind.
  • App roles: createE2eSeedRoutes({ extraRoles: ["TenantMember"] }) lets tenant.addUser(["TenantMember"]) seed them.
  • tenant.addUser(["SystemAdmin"]) seeds a platform operator for SysAdmin screens: SystemAdmin lands as a global user role, and the user joins the tenant as Member unless you also pass a tenant role (["TenantAdmin", "SystemAdmin"]). The seed gate above is the only boundary around this.
  • App seeders for test data the seeded users can't create: createE2eSeedRoutes({ extraSeeders: { checks: async (ctx, tenantId, body) => … } }), called from a flow as await tenant.seed("checks", { days: 30 }). The seeder gets body as unknown (validate it with a zod parse; a ZodError becomes a 400) and a ctx whose write/query run as the tenant's system user (SystemAdmin), bound to that one tenant, so the target handler must admit SystemAdmin (data no normal handler produces, like backdated history, needs a SystemAdmin-only handler). There is no raw DB access. Only tenants seeded by the same server's seed-tenant route are accepted; unknown seeder names are a 404.
  • A SeedPart receives { tenant } and works against the in-process seedTenant and the HTTP tenant alike.
  • Already have a custom server entry (mail transport, KMS, boot seeds, ...) that also runs in prod? Point serverEntry at it instead of duplicating e2e/server.ts, and mount the seed routes only when the seed condition holds: ...(isE2eSeedingEnabled() ? createE2eSeedRoutes() : []) in its own extraRoutes — prod then never registers the routes at all, not just a per-request 404. isE2eSeedingEnabled is exported from @cosmicdrift/kumiko-testing/e2e/seed-route.
  • App-wide defaults for tests (e.g. a completed onboarding) belong in a SeedPart passed to seedTenant({ with: [defaults] }) — never in a postSave hook on the "tenant" entity, since that hook also fires on every real signup in prod.

Screenshots (./e2e)

Seeded identities are unique per run (admin-<tenantId>@…). To show a presentable address instead, the flow registers the mapping once the tenant exists, and the runner replaces it in text and form values right before every capture (again after each theme and viewport change); the seeded data stays as it is: presentIdentities([{ from: tenant.admin.email, to: "[email protected]" }]) from the scenario fixtures, or captureScreenshot(page, name, { presentIdentities: [...] }) inline.

runScreenshots/runMatrix register their tests on the same test as above, so a scenario's flow receives the seeded-tenant fixture too:

{ name: "dispatch", flow: async (page, { seedTenant }) => {
    const tenant = await seedTenant({ users: 1 });
    const [member] = tenant.members;
    if (!member) throw new Error("seedTenant returned no member");
    await tenant.loginAs(page, member);
    await page.goto("/dispatch");
  },
  beforeCapture: async (page) => {
    await page.addStyleTag({ content: ".live-clock { visibility: hidden }" });
  },
}

beforeCapture?: (page) => Promise<void> runs after the viewport is set and the page has settled, right before the screenshot. runMatrix calls it once per theme × viewport for the same scenario, so it must be idempotent — hide or mask an element rather than a one-shot action like clicking a button, which would only succeed on the first capture and time out on every one after.

runMatrix also supports real device emulation via Playwright projects named after a viewport id (desktop, tablet, mobile) with use.isMobile: true:

projects: [
  { name: "desktop", use: { ...devices["Desktop Chrome"] } },
  { name: "tablet", use: { ...devices["iPad Pro 11"] } }, // portrait, WebKit
  { name: "mobile", use: { ...devices["iPhone 13"] } }, // portrait, WebKit
];

A device project captures exactly <name>.png at the device's native size — no setViewportSize, which would destroy the emulation. Any other project (the desktop pass) still loops the remaining viewports via setViewportSize, skipping the ids a device project already covers. Without device projects, nothing changes: desktop, tablet and mobile all render through setViewportSize as before. SCREENSHOT_VIEWPORTS still filters both — a filtered-out device project's test is skipped, not run empty. CI needs bunx playwright install webkit to run device projects that use a WebKit device.

版本与发布记录

@cosmicdrift/kumiko-testing 共发布 133 个版本,首次发布于 2026/09/21;过去 12 个月发布了 133 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.419.0latest2026/11/11今天
0.418.02026/11/101 天前
0.417.02026/11/091 天前
0.416.02026/11/082 天前
0.415.02026/11/064 天前
0.414.02026/11/055 天前

其他发布通道:kumiko-tmp → 0.355.0。

关于 @cosmicdrift/kumiko-testing 的常见问题

@cosmicdrift/kumiko-testing 是什么?

Test template for Kumiko apps: per-flow seedTenant, app test stack, canonical test preloads, bunfig generator and integration runner.

如何安装 @cosmicdrift/kumiko-testing?

在项目目录运行 npm install @cosmicdrift/kumiko-testing,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 kumiko-testing,可以用 npx kumiko-testing 直接运行。

@cosmicdrift/kumiko-testing 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@cosmicdrift/kumiko-testing 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@cosmicdrift/kumiko-testing 可以商用吗?

@cosmicdrift/kumiko-testing 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@cosmicdrift/kumiko-testing 的下载量有多少?

根据 npm 官方下载统计,@cosmicdrift/kumiko-testing 在 2026/10/11 – 2026/11/09 这 30 天内被下载 20,684 次,日均约 689 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@cosmicdrift/kumiko-testing 还在维护吗?

最新版本 0.419.0 发布于 2026/11/11(今天);至今共发布 133 个版本,其中过去 12 个月发布了 133 个;首次发布于 2026/09/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。