Component test writer
Generates component-level unit tests from source files and (optionally) a scenario-set.md. Framework-aware (vitest | jest) via repo-fingerprint, never guesses. Enforces strict Arrange-Act-Assert structure, preserves every scenario as a test case, and refuses to overwrite existing tests. PIPELINE-1 step 4.From its SKILL.md
npx -y skills add mytechsonamy/VibeFlow --skill component-test-writerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.2 KB, as published. Nobody here has run it
Component Test Writer
Phase Contract
This skill runs in DEVELOPMENT or TESTING only. Before any other
step, read vibeflow.config.json's currentPhase. If it is not in
{DEVELOPMENT, TESTING}, emit:
component-test-writer is for DEVELOPMENT or TESTING phase; current is
<phase>. Run/vibeflow:test-strategy-planner(PLANNING) first and/vibeflow:advanceinto DEVELOPMENT before writing tests.
…and stop. The PreToolUse phase-write-guard hook will also reject any
test-file writes this skill attempts outside those phases.
An L1 Truth-Validation skill. Takes a source file (or a small set of files) and produces test files whose structure the reader can audit in fifteen seconds. This skill does not run tests — it only generates them.
When You're Invoked
- During PIPELINE-1 step 4 after
test-strategy-plannerhas produced ascenario-set.md. In that pipeline, this skill runs once per component in parallel withcontract-test-writer,business-rule-validator, andtest-data-manager. - On demand as
/vibeflow:component-test-writer <path-or-glob>.
Input Contract
| Input | Required | Notes |
|---|---|---|
| Source file(s) | yes | Path, glob, or directory. Non-test files only. |
scenario-set.md | optional but preferred | Output of test-strategy-planner; maps scenarios → test cases. |
| Framework config | derived | Read from repo-fingerprint.json or detected via ci_analyze_structure. Never hardcoded. |
| Existing tests | scanned | Every existing test file is read before writing a new one so we do not clobber human work. |
Hard preconditions — refuse to run with a single blocks-merge finding rather than producing garbage:
- The source file(s) must exist and parse as the detected language.
- The framework must be known. If detection returns 0 runners, fail
with remediation: "run
/vibeflow:repo-fingerprintfirst, or add a vitest/jest config file to the repo root." - A test file that already exists and was NOT previously generated by this skill is treated as sealed — we add cases by appending to it, never by rewriting the whole file.
Algorithm
Step 1 — Discover the framework
Order matters:
- Read
.vibeflow/artifacts/repo-fingerprint.jsonif present. - Otherwise call
ci_analyze_structureon the codebase-intel MCP. - Otherwise look for a
vitest.config.*orjest.config.*file in the project root. - Otherwise fail (see preconditions).
Record the chosen framework + the evidence path so the report can cite
it. framework-recipes.md maps the detected framework to its import
syntax, hook shape, and assertion library.
Step 2 — Read and classify the source
For each input source file, enumerate:
- Exports (named functions, default class, constants)
- Public API (what a caller could reasonably import and use)
- Side effects (I/O, network, clock, RNG, globals)
- Dependencies (imports that may need mocking)
Refuse to generate tests for files that have no exports or that only re-export — the right place for those is an integration test, not a component test.
Step 3 — Map scenarios to cases
If scenario-set.md exists, every scenario whose "target component"
field matches the current source file becomes a candidate test case.
A scenario becomes a test case only when:
- The scenario has a measurable expected outcome (an assertion the test can make).
- The scenario's inputs can be constructed from data already in
fixtures OR can be generated by
test-data-managerin a follow-up step. If not, mark the case aspending: "awaiting test-data"and emit it withit.skip— never invent fixture values. - The scenario is not already covered by an existing test (simple substring match on the scenario id in the test file).
If scenario-set.md is absent, derive cases from the public API:
- one happy path per exported function
- one boundary case per numeric / string-length / array-length argument
- one failure case per declared thrown error class
- one concurrency case if the source uses async + shared state
Step 4 — Enforce Arrange-Act-Assert
Every generated it(...) block must have three comment-delimited
sections in this exact order:
it("returns the zero value when the list is empty", () => {
// Arrange
const input: number[] = [];
// Act
const result = sumList(input);
// Assert
expect(result).toBe(0);
});
Rules:
- Arrange MUST NOT call the system under test.
- Act is exactly one line. If setup bleeds into Act, extract a helper.
- Assert MUST NOT call the system under test — only check its output.
- No bare
expect.assertions(n)hacks to paper over async awaits.
See references/test-patterns.md for parametrized, table-driven, and
mock-heavy variants; never deviate from the three-section shape.
Step 5 — Scenario traceability
Every generated it(...) title must start with the scenario id when
one exists, e.g. it("SC-017: returns the zero value when the list is empty", ...).
The it body ends with an explainability comment:
// trace: scenarios/SC-017 — "empty list aggregation"
// why: guards against a regression from PRD#3.2 (aggregation pipeline)
When the skill's output is later consumed by traceability-engine, the
trace: line is the only thing the engine needs to wire the test back
to the scenario and the PRD.
Step 6 — Write the test file
Target path convention: for source src/foo/bar.ts, write to
src/foo/bar.test.ts (sibling-with-.test). If a project already uses
a different layout (e.g. tests/ mirror), preserve that layout — check
a few existing tests to decide.
Every generated file starts with a banner:
// @generated-by vibeflow:component-test-writer
// Regenerate with: /vibeflow:component-test-writer <source path>
// Do NOT edit the @generated regions by hand — they will be overwritten.
Regions between // @generated-start and // @generated-end markers
are owned by the skill. Anything outside those markers is human-owned
and will be preserved verbatim on re-run. This is how we keep
regenerating tests safely.
Step 7 — Emit a run report
Append a summary to .vibeflow/reports/component-test-writer.md:
# Component Test Writer — <ISO timestamp>
## Target(s)
- src/foo/bar.ts (framework: vitest)
## Test file(s)
- src/foo/bar.test.ts (created | appended | no-op)
## Cases generated
- SC-017: returns the zero value when the list is empty → it(...)
- SC-018: sums a non-empty list → it(...)
- SC-019: rejects non-numeric input → it.skip (awaiting test-data)
## Scenarios skipped with reason
- SC-020 (target mismatch: scenario is for src/foo/baz.ts)
## Framework evidence
- repo-fingerprint.json (primary)
Output Contract
Every emitted test file:
- has the
@generated-bybanner - wraps the skill-owned region with
@generated-start/@generated-end - uses only the detected framework's imports (no mixing)
- is runnable with zero edits (no TODO-comments blocking the build)
- preserves pre-existing cases verbatim
Explainability contract applies to the run report, not to individual
it blocks: each report entry records finding / why / impact / confidence
for decisions the skill made (e.g. skipping a scenario, choosing an
unusual mock strategy).
Non-Goals
- Does NOT generate integration tests — that's
contract-test-writer(for API contracts) ore2e-test-writer(Sprint 3). - Does NOT generate fixtures or factories — that's
test-data-manager. - Does NOT run the generated tests — wire them into CI yourself.
Downstream Dependencies
traceability-engine— consumes thetrace:lines to keep the RTM currenttest-priority-engine— uses the generated file list to rank affected testsmutation-test-runner— runs mutations against the generated assertions