vitest-dev/vitest · error · TypeError

toHaveFormValues must be called with an object of expected…

Error message

toHaveFormValues must be called with an object of expected form values. Got ${expectedValues}

What it means

Thrown by the `toHaveFormValues` matcher when the second argument (`expectedValues`) is not a plain object. The matcher validates the argument with `!expectedValues || typeof expectedValues !== 'object'` before reading form fields, because the comparison logic iterates `Object.entries(expectedValues)` to match each key against the form's collected values. Passing a primitive, null, or undefined makes the matcher's per-field comparison meaningless, so it refuses to run.

Solutions

  1. Pass a plain object mapping each form field's `name` attribute to its expected value, e.g. `expect(form).toHaveFormValues({ email: 'a@b.com', count: 2 })`.
  2. If you have a JSON string, parse it first: `expect(form).toHaveFormValues(JSON.parse(str))`.
  3. Confirm each object key matches the DOM element's `name` attribute (not its `id`); unmatched keys cause assertion failure, not this error.
  4. For a single field use `toHaveValue` instead of `toHaveFormValues`.

Example fix

// before
expect(form).toHaveFormValues('a@b.com')

// after
expect(form).toHaveFormValues({ email: 'a@b.com' })
Defensive patterns

Strategy: validation

Validate before calling

// before calling the matcher
function assertFormValuesObject(v: unknown): asserts v is Record<string, unknown> {
  if (!v || typeof v !== 'object' || Array.isArray(v)) {
    throw new TypeError('toHaveFormValues expects a plain object of { fieldName: value }')
  }
}
assertFormValuesObject(expected)
expect(form).toHaveFormValues(expected)

Type guard

function isFormValuesObject(v: unknown): v is Record<string, unknown> {
  return !!v && typeof v === 'object' && !Array.isArray(v)
}

Prevention

When it happens

Trigger: Calling `expect(form).toHaveFormValues(null)`, `.toHaveFormValues(undefined)`, `.toHaveFormValues('email')` (string), `.toHaveFormValues(5)` (number), or omitting the argument entirely so it defaults to undefined. The check at toHaveFormValues.ts:34 fires before any DOM value is read.

Common situations: Forgetting the expected-values argument; passing a serialized JSON string instead of a parsed object; refactoring a test and accidentally passing the expected value of a single field instead of a `{ name: value }` record; copy-pasting a `toHaveValue` call (which takes a scalar) into a `toHaveFormValues` call without wrapping it.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/9ad86eaac9c2bfa7. Report an issue: GitHub.

Appendix: source

Thrown at packages/browser/src/client/tester/expect/toHaveFormValues.ts:35

import type { Locator } from '../locators'
import { cssEscape } from 'ivya/utils'
import { arrayAsSetComparison, getElementFromUserInput, getSingleElementValue, getTag } from './utils'

export default function toHaveFormValues(
  this: MatcherState,
  actual: Element | Locator,
  expectedValues: Record<string, unknown>,
): MatcherResult {
  const formElement = getElementFromUserInput(actual, toHaveFormValues, this)

  const defaultView = formElement.ownerDocument.defaultView || window

  if (!(formElement instanceof defaultView.HTMLFieldSetElement) && !(formElement instanceof defaultView.HTMLFormElement)) {
    throw new TypeError(`toHaveFormValues must be called on a form or a fieldset, instead got ${getTag(formElement)}`)
  }

  if (!expectedValues || typeof expectedValues !== 'object') {
    throw new TypeError(
      `toHaveFormValues must be called with an object of expected form values. Got ${expectedValues}`,
    )
  }

  const formValues = getAllFormValues(formElement)
  return {
    pass: Object.entries(expectedValues).every(([name, expectedValue]) =>
      this.equals(formValues[name], expectedValue, [arrayAsSetComparison, ...this.customTesters]),
    ),
    message: () => {
      const to = this.isNot ? 'not to' : 'to'
      const matcher = `${this.isNot ? '.not' : ''}.toHaveFormValues`

      const commonKeyValues: Record<string, unknown> = {}
      for (const key in formValues) {
        if (!Object.hasOwn(expectedValues, key)) {
          continue
        }

View on GitHub (pinned to 1fa9837ec2)