Skip to content

@unthrown/vitest: add toBeDefectWith(cause) to complete the matcher set #194

Description

@btravers

@unthrown/vitest ships six matchers. The Defect channel is the only one
without a value assertion:

channel existence value
Ok toBeOk toBeOkWith(value)
Err toBeErr toBeErrWith(error), toBeErrTagged(tag, payload?)
Defect toBeDefect

Asserting what caused a defect currently takes a two-step, and the narrowing
step only works on a synchronous Result — on an AsyncResult the value has to
be awaited separately, which is exactly what the matchers exist to avoid:

const r = await someOperation();
expect(r).toBeDefect();
if (r.isDefect()) expect(r.cause).toBeInstanceOf(TypeError);

Proposal

Add toBeDefectWith(cause), mirroring toBeErrWith
(packages/vitest/src/index.ts:270) exactly:

function toBeDefectWith(this: MatcherState, received: unknown, expected: unknown): MatcherResult {
  const { stringify } = this.utils;
  const { equals } = this;
  return settle("toBeDefectWith", this, received, (result) => {
    const pass = isDefect(result) && equals(result.cause, expected);
    
  });
}

Going through settle means it inherits the thenable handling and the
forgotten-await net for free, like every other matcher.

Usage:

await expect(asyncResult).toBeDefectWith(expect.any(TypeError));
expect(result).toBeDefectWith(theOriginalCause);

expected is unknown, matching the channel: a defect's cause is unknown by
design, so there is no tighter type to give it and no tag-aware variant to add
(the toBeErrTagged counterpart has no meaning here).

Checklist

  • expect.extend registration and the raw-function export (index.ts:341-343).
  • The UnthrownMatchers type (index.ts:397-399).
  • CLAUDE.md — the vitest package description says "the six raw matcher
    functions" and the status section enumerates them.
  • docs/how-to/test-with-vitest.md and the skills/unthrown/ copy, in the same
    PR (the skill is hand-maintained and drifts).
  • A changeset.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions