vitest-dev/vitest · error · TypeError

toHaveFormValues must be called on a form or a fieldset, ins

Error message

toHaveFormValues must be called on a form or a fieldset, instead got ${getTag(formElement)}

What it means

Thrown by the `toHaveFormValues` matcher when the received element is not an HTMLFormElement or HTMLFieldSetElement. The matcher inspects the DOM element returned from the user's locator/element input and checks it against `defaultView.HTMLFieldSetElement`/`HTMLFormElement` at toHaveFormValues.ts:30; any other tag fails. This is a precondition violation, not an assertion failure — the matcher cannot read form values off a non-form node.

Source

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

 * copies or substantial portions of the Software.
 */

import type { MatcherResult, MatcherState } from 'vitest'
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> = {}

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Pass the `<form>` or `<fieldset>` element/locator to `expect()` instead of an input, select, or div inside it.
  2. If using a Locator, ensure it resolves to the form: `page.getByRole('form')` or `page.locator('form#login')`.
  3. If the element is inside a shadow root or iframe, resolve the form in that document context before calling the matcher.

Example fix

// before
const input = page.getByRole('textbox', { name: 'email' })
expect(input).toHaveFormValues({ email: 'a@b.com' })

// after
const form = page.getByRole('form', { name: 'Sign in' })
expect(form).toHaveFormValues({ email: 'a@b.com' })
Defensive patterns

Strategy: validation

Validate before calling

function isFormLike(el: Element | null | undefined, view: Window = window): boolean {
  return !!el && (el instanceof view.HTMLFormElement || el instanceof view.HTMLFieldSetElement)
}
// before calling:
if (!isFormLike(target)) throw new Error('pass a <form>/<fieldset> to toHaveFormValues')

Type guard

function isFormOrFieldSet(el: unknown, view: Window = window): el is HTMLFormElement | HTMLFieldSetElement {
  return el instanceof view.HTMLFormElement || el instanceof view.HTMLFieldSetElement
}

Prevention

When it happens

Trigger: Calling `expect(div).toHaveFormValues({...})`, `expect(page.getByRole('textbox')).toHaveFormValues(...)`, or passing any element whose tag is not `<form>` or `<fieldset>`. Also triggered when a Locator resolves to a child input inside the form instead of the form/fieldset itself.

Common situations: Developers coming from jest-dom where they pass a form *child* (e.g. an `<input>`) rather than the wrapping `<form>`. Also happens when `page.getByRole('form')` matches nothing and a fallback element slips through, or when fieldset detection fails because the element lives in a different document (shadow DOM/iframe) whose `defaultView` differs.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/5f77490251cbd310.json. Report an issue: GitHub.