feat: add toHaveAccessibleErrorMessage
This commit is contained in:
parent
37e155a8a1
commit
b3e3545383
|
|
@ -29,7 +29,7 @@ import type { CSSComplexSelectorList } from '../../utils/isomorphic/cssParser';
|
|||
import { generateSelector, type GenerateSelectorOptions } from './selectorGenerator';
|
||||
import type * as channels from '@protocol/channels';
|
||||
import { Highlight } from './highlight';
|
||||
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription, getReadonly } from './roleUtils';
|
||||
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription, getReadonly, getElementAccessibleErrorMessage } from './roleUtils';
|
||||
import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils';
|
||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
||||
|
|
@ -1321,6 +1321,8 @@ export class InjectedScript {
|
|||
received = getElementAccessibleName(element, false /* includeHidden */);
|
||||
} else if (expression === 'to.have.accessible.description') {
|
||||
received = getElementAccessibleDescription(element, false /* includeHidden */);
|
||||
} else if (expression === 'to.have.accessible.error.message') {
|
||||
received = getElementAccessibleErrorMessage(element, false /* includeHidden */);
|
||||
} else if (expression === 'to.have.role') {
|
||||
received = getAriaRole(element) || '';
|
||||
} else if (expression === 'to.have.title') {
|
||||
|
|
|
|||
|
|
@ -461,6 +461,38 @@ export function getElementAccessibleDescription(element: Element, includeHidden:
|
|||
return accessibleDescription;
|
||||
}
|
||||
|
||||
export function getElementAccessibleErrorMessage(element: Element, includeHidden: boolean): string {
|
||||
const cache = includeHidden ? cacheAccessibleErrorMessageHidden : cacheAccessibleErrorMessage;
|
||||
let accessibleErrorMessage = cache?.get(element);
|
||||
|
||||
if (accessibleErrorMessage === undefined) {
|
||||
accessibleErrorMessage = '';
|
||||
|
||||
const ariaInvalid = element.getAttribute('aria-invalid');
|
||||
if (ariaInvalid === 'true') {
|
||||
const errorMessageId = element.getAttribute('aria-errormessage');
|
||||
if (errorMessageId) {
|
||||
// Ensure the ID is valid (no whitespace)
|
||||
if (!/\s+/.test(errorMessageId)) {
|
||||
// Retrieve the element referenced by aria-errormessage.
|
||||
const errorElement = element.ownerDocument.getElementById(errorMessageId);
|
||||
if (errorElement) {
|
||||
accessibleErrorMessage = asFlatString(
|
||||
getTextAlternativeInternal(errorElement, {
|
||||
includeHidden,
|
||||
visitedElements: new Set(),
|
||||
embeddedInDescribedBy: { element: errorElement, hidden: isElementHiddenForAria(errorElement) },
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cache?.set(element, accessibleErrorMessage);
|
||||
}
|
||||
return accessibleErrorMessage;
|
||||
}
|
||||
|
||||
type AccessibleNameOptions = {
|
||||
visitedElements: Set<Element>,
|
||||
includeHidden?: boolean,
|
||||
|
|
@ -972,6 +1004,8 @@ let cacheAccessibleName: Map<Element, string> | undefined;
|
|||
let cacheAccessibleNameHidden: Map<Element, string> | undefined;
|
||||
let cacheAccessibleDescription: Map<Element, string> | undefined;
|
||||
let cacheAccessibleDescriptionHidden: Map<Element, string> | undefined;
|
||||
let cacheAccessibleErrorMessage: Map<Element, string> | undefined;
|
||||
let cacheAccessibleErrorMessageHidden: Map<Element, string> | undefined;
|
||||
let cacheIsHidden: Map<Element, boolean> | undefined;
|
||||
let cachePseudoContentBefore: Map<Element, string> | undefined;
|
||||
let cachePseudoContentAfter: Map<Element, string> | undefined;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import {
|
|||
toContainText,
|
||||
toHaveAccessibleDescription,
|
||||
toHaveAccessibleName,
|
||||
toHaveAccessibleErrorMessage,
|
||||
toHaveAttribute,
|
||||
toHaveClass,
|
||||
toHaveCount,
|
||||
|
|
@ -224,6 +225,7 @@ const customAsyncMatchers = {
|
|||
toContainText,
|
||||
toHaveAccessibleDescription,
|
||||
toHaveAccessibleName,
|
||||
toHaveAccessibleErrorMessage,
|
||||
toHaveAttribute,
|
||||
toHaveClass,
|
||||
toHaveCount,
|
||||
|
|
|
|||
|
|
@ -205,6 +205,18 @@ export function toHaveAccessibleName(
|
|||
}
|
||||
}
|
||||
|
||||
export function toHaveAccessibleErrorMessage(
|
||||
this: ExpectMatcherState,
|
||||
locator: LocatorEx,
|
||||
expected: string | RegExp,
|
||||
options?: { timeout?: number; ignoreCase?: boolean },
|
||||
) {
|
||||
return toMatchText.call(this, 'toHaveAccessibleErrorMessage', locator, 'Locator', async (isNot, timeout) => {
|
||||
const expectedText = serializeExpectedTextValues([expected], { ignoreCase: options?.ignoreCase });
|
||||
return await locator._expect('to.have.accessible.error.message', { expectedText: expectedText, isNot, timeout });
|
||||
}, expected, options);
|
||||
}
|
||||
|
||||
export function toHaveAttribute(
|
||||
this: ExpectMatcherState,
|
||||
locator: LocatorEx,
|
||||
|
|
|
|||
Loading…
Reference in a new issue