···476476 readonly selector: string
477477478478 /**
479479+ * The number of elements that this locator is matching.
480480+ * @see {@link https://vitest.dev/guide/browser/locators#length}
481481+ */
482482+ readonly length: number
483483+484484+ /**
479485 * Click on an element. You can use the options to set the cursor position.
480486 * @see {@link https://vitest.dev/guide/browser/interactivity-api#userevent-click}
481487 */
+19
docs/guide/browser/locators.md
···956956```
957957:::
958958959959+### length
960960+961961+This getter returns a number of elements that this locator is matching. It is equivalent to calling `locator.elements().length`.
962962+963963+Consider the following DOM structure:
964964+965965+```html
966966+<button>Click Me!</button>
967967+<button>Don't click me!</button>
968968+```
969969+970970+This property will always succeed:
971971+972972+```ts
973973+page.getByRole('button').length // ✅ 2
974974+page.getByRole('button', { title: 'Click Me!' }).length // ✅ 1
975975+page.getByRole('alert').length // ✅ 0
976976+```
977977+959978## Custom Locators <Version>3.2.0</Version> <Badge type="danger">advanced</Badge> {#custom-locators}
960979961980You can extend built-in locators API by defining an object of locator factories. These methods will exist as methods on the `page` object and any created locator.
···11import type { Locator } from '@vitest/browser/context'
22import type { ExpectPollOptions } from 'vitest'
33import { chai, expect } from 'vitest'
44+import { getType } from 'vitest/internal/browser'
45import { matchers } from './expect'
56import { processTimeoutOptions } from './utils'
6777-function element<T extends Element | null | Locator>(elementOrLocator: T, options?: ExpectPollOptions): unknown {
88- if (elementOrLocator != null && !(elementOrLocator instanceof Element) && !('element' in elementOrLocator)) {
99- throw new Error(`Invalid element or locator: ${elementOrLocator}. Expected an instance of Element or Locator, received ${typeof elementOrLocator}`)
88+const kLocator = Symbol.for('$$vitest:locator')
99+1010+function element<T extends HTMLElement | SVGElement | null | Locator>(elementOrLocator: T, options?: ExpectPollOptions): unknown {
1111+ if (elementOrLocator != null && !(elementOrLocator instanceof HTMLElement) && !(elementOrLocator instanceof SVGElement) && !(kLocator in elementOrLocator)) {
1212+ throw new Error(`Invalid element or locator: ${elementOrLocator}. Expected an instance of HTMLElement, SVGElement or Locator, received ${getType(elementOrLocator)}`)
1013 }
11141212- return expect.poll<Element | null>(function element(this: object) {
1515+ return expect.poll<HTMLElement | SVGElement | null>(function element(this: object) {
1316 if (elementOrLocator instanceof Element || elementOrLocator == null) {
1417 return elementOrLocator
1518 }
···17201821 const isNot = chai.util.flag(this, 'negate') as boolean
1922 const name = chai.util.flag(this, '_name') as string
2020- // element selector uses prettyDOM under the hood, which is an expensive call
2121- // that should not be called on each failed locator attempt to avoid memory leak:
2222- // https://github.com/vitest-dev/vitest/issues/7139
2323- const isLastPollAttempt = chai.util.flag(this, '_isLastPollAttempt')
2423 // special case for `toBeInTheDocument` matcher
2524 if (isNot && name === 'toBeInTheDocument') {
2625 return elementOrLocator.query()
2726 }
2727+ if (name === 'toHaveLength') {
2828+ // we know that `toHaveLength` requires multiple elements,
2929+ // but types generally expect a single one
3030+ return elementOrLocator.elements() as unknown as HTMLElement
3131+ }
3232+3333+ // element selector uses prettyDOM under the hood, which is an expensive call
3434+ // that should not be called on each failed locator attempt to avoid memory leak:
3535+ // https://github.com/vitest-dev/vitest/issues/7139
3636+ const isLastPollAttempt = chai.util.flag(this, '_isLastPollAttempt')
28372938 if (isLastPollAttempt) {
3039 return elementOrLocator.element()