chore: include actual value in the elementState (#34245)
This commit is contained in:
parent
0008816ee3
commit
0d34369fc0
|
|
@ -778,7 +778,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions): Promise<'error:notconnected' | 'done'> {
|
||||||
const isChecked = async () => {
|
const isChecked = async () => {
|
||||||
const result = await this.evaluateInUtility(([injected, node]) => injected.elementState(node, 'checked'), {});
|
const result = await this.evaluateInUtility(([injected, node]) => injected.elementState(node, 'checked'), {});
|
||||||
return throwRetargetableDOMError(result);
|
if (result === 'error:notconnected' || result.received === 'error:notconnected')
|
||||||
|
throwElementIsNotAttached();
|
||||||
|
return result.matches;
|
||||||
};
|
};
|
||||||
await this._markAsTargetElement(progress.metadata);
|
await this._markAsTargetElement(progress.metadata);
|
||||||
if (await isChecked() === state)
|
if (await isChecked() === state)
|
||||||
|
|
@ -913,10 +915,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
|
|
||||||
export function throwRetargetableDOMError<T>(result: T | 'error:notconnected'): T {
|
export function throwRetargetableDOMError<T>(result: T | 'error:notconnected'): T {
|
||||||
if (result === 'error:notconnected')
|
if (result === 'error:notconnected')
|
||||||
throw new Error('Element is not attached to the DOM');
|
throwElementIsNotAttached();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function throwElementIsNotAttached(): never {
|
||||||
|
throw new Error('Element is not attached to the DOM');
|
||||||
|
}
|
||||||
|
|
||||||
export function assertDone(result: 'done'): void {
|
export function assertDone(result: 'done'): void {
|
||||||
// This function converts 'done' to void and ensures typescript catches unhandled errors.
|
// This function converts 'done' to void and ensures typescript catches unhandled errors.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1301,7 +1301,9 @@ export class Frame extends SdkObject {
|
||||||
const result = await this._callOnElementOnceMatches(metadata, selector, (injected, element, data) => {
|
const result = await this._callOnElementOnceMatches(metadata, selector, (injected, element, data) => {
|
||||||
return injected.elementState(element, data.state);
|
return injected.elementState(element, data.state);
|
||||||
}, { state }, options, scope);
|
}, { state }, options, scope);
|
||||||
return dom.throwRetargetableDOMError(result);
|
if (result.received === 'error:notconnected')
|
||||||
|
dom.throwElementIsNotAttached();
|
||||||
|
return result.matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
async isVisible(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise<boolean> {
|
async isVisible(metadata: CallMetadata, selector: string, options: types.StrictOptions = {}, scope?: dom.ElementHandle): Promise<boolean> {
|
||||||
|
|
@ -1319,8 +1321,8 @@ export class Frame extends SdkObject {
|
||||||
return false;
|
return false;
|
||||||
return await resolved.injected.evaluate((injected, { info, root }) => {
|
return await resolved.injected.evaluate((injected, { info, root }) => {
|
||||||
const element = injected.querySelector(info.parsed, root || document, info.strict);
|
const element = injected.querySelector(info.parsed, root || document, info.strict);
|
||||||
const state = element ? injected.elementState(element, 'visible') : false;
|
const state = element ? injected.elementState(element, 'visible') : { matches: false, received: 'error:notconnected' };
|
||||||
return state === 'error:notconnected' ? false : state;
|
return state.matches;
|
||||||
}, { info: resolved.info, root: resolved.frame === this ? scope : undefined });
|
}, { info: resolved.info, root: resolved.frame === this ? scope : undefined });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (js.isJavaScriptErrorInEvaluate(e) || isInvalidSelectorError(e) || isSessionClosedError(e))
|
if (js.isJavaScriptErrorInEvaluate(e) || isInvalidSelectorError(e) || isSessionClosedError(e))
|
||||||
|
|
@ -1809,26 +1811,6 @@ function verifyLifecycle(name: string, waitUntil: types.LifecycleEvent): types.L
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderUnexpectedValue(expression: string, received: any): string {
|
function renderUnexpectedValue(expression: string, received: any): string {
|
||||||
if (expression === 'to.be.checked')
|
|
||||||
return received ? 'checked' : 'unchecked';
|
|
||||||
if (expression === 'to.be.unchecked')
|
|
||||||
return received ? 'unchecked' : 'checked';
|
|
||||||
if (expression === 'to.be.visible')
|
|
||||||
return received ? 'visible' : 'hidden';
|
|
||||||
if (expression === 'to.be.hidden')
|
|
||||||
return received ? 'hidden' : 'visible';
|
|
||||||
if (expression === 'to.be.enabled')
|
|
||||||
return received ? 'enabled' : 'disabled';
|
|
||||||
if (expression === 'to.be.disabled')
|
|
||||||
return received ? 'disabled' : 'enabled';
|
|
||||||
if (expression === 'to.be.editable')
|
|
||||||
return received ? 'editable' : 'readonly';
|
|
||||||
if (expression === 'to.be.readonly')
|
|
||||||
return received ? 'readonly' : 'editable';
|
|
||||||
if (expression === 'to.be.empty')
|
|
||||||
return received ? 'empty' : 'not empty';
|
|
||||||
if (expression === 'to.be.focused')
|
|
||||||
return received ? 'focused' : 'not focused';
|
|
||||||
if (expression === 'to.match.aria')
|
if (expression === 'to.match.aria')
|
||||||
return received ? received.raw : received;
|
return received ? received.raw : received;
|
||||||
return received;
|
return received;
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,9 @@ import { parseYamlTemplate } from '@isomorphic/ariaSnapshot';
|
||||||
|
|
||||||
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
||||||
|
|
||||||
export type ElementStateWithoutStable = 'visible' | 'hidden' | 'enabled' | 'disabled' | 'editable' | 'checked' | 'unchecked';
|
export type ElementState = 'visible' | 'hidden' | 'enabled' | 'disabled' | 'editable' | 'checked' | 'unchecked' | 'mixed' | 'stable';
|
||||||
export type ElementState = ElementStateWithoutStable | 'stable';
|
export type ElementStateWithoutStable = Exclude<ElementState, 'stable'>;
|
||||||
|
export type ElementStateQueryResult = { matches: boolean, received?: string | 'error:notconnected' };
|
||||||
|
|
||||||
export type HitTargetInterceptionResult = {
|
export type HitTargetInterceptionResult = {
|
||||||
stop: () => 'done' | { hitTargetDescription: string };
|
stop: () => 'done' | { hitTargetDescription: string };
|
||||||
|
|
@ -545,15 +546,15 @@ export class InjectedScript {
|
||||||
if (stableResult === false)
|
if (stableResult === false)
|
||||||
return { missingState: 'stable' };
|
return { missingState: 'stable' };
|
||||||
if (stableResult === 'error:notconnected')
|
if (stableResult === 'error:notconnected')
|
||||||
return stableResult;
|
return 'error:notconnected';
|
||||||
}
|
}
|
||||||
for (const state of states) {
|
for (const state of states) {
|
||||||
if (state !== 'stable') {
|
if (state !== 'stable') {
|
||||||
const result = this.elementState(node, state);
|
const result = this.elementState(node, state);
|
||||||
if (result === false)
|
if (result.received === 'error:notconnected')
|
||||||
|
return 'error:notconnected';
|
||||||
|
if (!result.matches)
|
||||||
return { missingState: state };
|
return { missingState: state };
|
||||||
if (result === 'error:notconnected')
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -608,38 +609,50 @@ export class InjectedScript {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
elementState(node: Node, state: ElementStateWithoutStable): boolean | 'error:notconnected' {
|
elementState(node: Node, state: ElementStateWithoutStable): ElementStateQueryResult {
|
||||||
const element = this.retarget(node, ['stable', 'visible', 'hidden'].includes(state) ? 'none' : 'follow-label');
|
const element = this.retarget(node, ['visible', 'hidden'].includes(state) ? 'none' : 'follow-label');
|
||||||
if (!element || !element.isConnected) {
|
if (!element || !element.isConnected) {
|
||||||
if (state === 'hidden')
|
if (state === 'hidden')
|
||||||
return true;
|
return { matches: true, received: 'hidden' };
|
||||||
return 'error:notconnected';
|
return { matches: false, received: 'error:notconnected' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state === 'visible')
|
if (state === 'visible' || state === 'hidden') {
|
||||||
return isElementVisible(element);
|
const visible = isElementVisible(element);
|
||||||
if (state === 'hidden')
|
return {
|
||||||
return !isElementVisible(element);
|
matches: state === 'visible' ? visible : !visible,
|
||||||
|
received: visible ? 'visible' : 'hidden'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === 'disabled' || state === 'enabled') {
|
||||||
const disabled = getAriaDisabled(element);
|
const disabled = getAriaDisabled(element);
|
||||||
if (state === 'disabled')
|
return {
|
||||||
return disabled;
|
matches: state === 'disabled' ? disabled : !disabled,
|
||||||
if (state === 'enabled')
|
received: disabled ? 'disabled' : 'enabled'
|
||||||
return !disabled;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (state === 'editable') {
|
if (state === 'editable') {
|
||||||
|
const disabled = getAriaDisabled(element);
|
||||||
const readonly = getReadonly(element);
|
const readonly = getReadonly(element);
|
||||||
if (readonly === 'error')
|
if (readonly === 'error')
|
||||||
throw this.createStacklessError('Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]');
|
throw this.createStacklessError('Element is not an <input>, <textarea>, <select> or [contenteditable] and does not have a role allowing [aria-readonly]');
|
||||||
return !disabled && !readonly;
|
return {
|
||||||
|
matches: !disabled && !readonly,
|
||||||
|
received: disabled ? 'disabled' : readonly ? 'readOnly' : 'editable'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state === 'checked' || state === 'unchecked') {
|
if (state === 'checked' || state === 'unchecked' || state === 'mixed') {
|
||||||
const need = state === 'checked';
|
const need = state === 'checked' ? true : state === 'unchecked' ? false : 'mixed';
|
||||||
const checked = getChecked(element, false);
|
const checked = getChecked(element, false);
|
||||||
if (checked === 'error')
|
if (checked === 'error')
|
||||||
throw this.createStacklessError('Not a checkbox or radio button');
|
throw this.createStacklessError('Not a checkbox or radio button');
|
||||||
return need === checked;
|
return {
|
||||||
|
matches: need === checked,
|
||||||
|
received: checked === true ? 'checked' : checked === false ? 'unchecked' : 'mixed',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
throw this.createStacklessError(`Unexpected element state "${state}"`);
|
throw this.createStacklessError(`Unexpected element state "${state}"`);
|
||||||
}
|
}
|
||||||
|
|
@ -1220,44 +1233,60 @@ export class InjectedScript {
|
||||||
|
|
||||||
{
|
{
|
||||||
// Element state / boolean values.
|
// Element state / boolean values.
|
||||||
let elementState: boolean | 'error:notconnected' | 'error:notcheckbox' | undefined;
|
let result: ElementStateQueryResult | undefined;
|
||||||
if (expression === 'to.have.attribute') {
|
if (expression === 'to.have.attribute') {
|
||||||
elementState = element.hasAttribute(options.expressionArg);
|
const hasAttribute = element.hasAttribute(options.expressionArg);
|
||||||
|
result = {
|
||||||
|
matches: hasAttribute,
|
||||||
|
received: hasAttribute ? 'attribute present' : 'attribute not present',
|
||||||
|
};
|
||||||
} else if (expression === 'to.be.checked') {
|
} else if (expression === 'to.be.checked') {
|
||||||
elementState = this.elementState(element, 'checked');
|
result = this.elementState(element, 'checked');
|
||||||
} else if (expression === 'to.be.unchecked') {
|
} else if (expression === 'to.be.unchecked') {
|
||||||
elementState = this.elementState(element, 'unchecked');
|
result = this.elementState(element, 'unchecked');
|
||||||
} else if (expression === 'to.be.disabled') {
|
} else if (expression === 'to.be.disabled') {
|
||||||
elementState = this.elementState(element, 'disabled');
|
result = this.elementState(element, 'disabled');
|
||||||
} else if (expression === 'to.be.editable') {
|
} else if (expression === 'to.be.editable') {
|
||||||
elementState = this.elementState(element, 'editable');
|
result = this.elementState(element, 'editable');
|
||||||
} else if (expression === 'to.be.readonly') {
|
} else if (expression === 'to.be.readonly') {
|
||||||
elementState = !this.elementState(element, 'editable');
|
result = this.elementState(element, 'editable');
|
||||||
|
result.matches = !result.matches;
|
||||||
} else if (expression === 'to.be.empty') {
|
} else if (expression === 'to.be.empty') {
|
||||||
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA')
|
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
|
||||||
elementState = !(element as HTMLInputElement).value;
|
const value = (element as HTMLInputElement).value;
|
||||||
else
|
result = { matches: !value, received: value ? 'notEmpty' : 'empty' };
|
||||||
elementState = !element.textContent?.trim();
|
} else {
|
||||||
|
const text = element.textContent?.trim();
|
||||||
|
result = { matches: !text, received: text ? 'notEmpty' : 'empty' };
|
||||||
|
}
|
||||||
} else if (expression === 'to.be.enabled') {
|
} else if (expression === 'to.be.enabled') {
|
||||||
elementState = this.elementState(element, 'enabled');
|
result = this.elementState(element, 'enabled');
|
||||||
} else if (expression === 'to.be.focused') {
|
} else if (expression === 'to.be.focused') {
|
||||||
elementState = this._activelyFocused(element).isFocused;
|
const focused = this._activelyFocused(element).isFocused;
|
||||||
|
result = {
|
||||||
|
matches: focused,
|
||||||
|
received: focused ? 'focused' : 'inactive',
|
||||||
|
};
|
||||||
} else if (expression === 'to.be.hidden') {
|
} else if (expression === 'to.be.hidden') {
|
||||||
elementState = this.elementState(element, 'hidden');
|
result = this.elementState(element, 'hidden');
|
||||||
} else if (expression === 'to.be.visible') {
|
} else if (expression === 'to.be.visible') {
|
||||||
elementState = this.elementState(element, 'visible');
|
result = this.elementState(element, 'visible');
|
||||||
} else if (expression === 'to.be.attached') {
|
} else if (expression === 'to.be.attached') {
|
||||||
elementState = true;
|
result = {
|
||||||
|
matches: true,
|
||||||
|
received: 'attached',
|
||||||
|
};
|
||||||
} else if (expression === 'to.be.detached') {
|
} else if (expression === 'to.be.detached') {
|
||||||
elementState = false;
|
result = {
|
||||||
|
matches: false,
|
||||||
|
received: 'attached',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementState !== undefined) {
|
if (result) {
|
||||||
if (elementState === 'error:notcheckbox')
|
if (result.received === 'error:notconnected')
|
||||||
throw this.createStacklessError('Element is not a checkbox');
|
|
||||||
if (elementState === 'error:notconnected')
|
|
||||||
throw this.createStacklessError('Element is not connected');
|
throw this.createStacklessError('Element is not connected');
|
||||||
return { received: elementState, matches: elementState };
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,8 @@ export function toBeAttached(
|
||||||
) {
|
) {
|
||||||
const attached = !options || options.attached === undefined || options.attached;
|
const attached = !options || options.attached === undefined || options.attached;
|
||||||
const expected = attached ? 'attached' : 'detached';
|
const expected = attached ? 'attached' : 'detached';
|
||||||
const unexpected = attached ? 'detached' : 'attached';
|
|
||||||
const arg = attached ? '' : '{ attached: false }';
|
const arg = attached ? '' : '{ attached: false }';
|
||||||
return toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||||
return await locator._expect(attached ? 'to.be.attached' : 'to.be.detached', { isNot, timeout });
|
return await locator._expect(attached ? 'to.be.attached' : 'to.be.detached', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -56,9 +55,8 @@ export function toBeChecked(
|
||||||
) {
|
) {
|
||||||
const checked = !options || options.checked === undefined || options.checked;
|
const checked = !options || options.checked === undefined || options.checked;
|
||||||
const expected = checked ? 'checked' : 'unchecked';
|
const expected = checked ? 'checked' : 'unchecked';
|
||||||
const unexpected = checked ? 'unchecked' : 'checked';
|
|
||||||
const arg = checked ? '' : '{ checked: false }';
|
const arg = checked ? '' : '{ checked: false }';
|
||||||
return toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||||
return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', { isNot, timeout });
|
return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +66,7 @@ export function toBeDisabled(
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
return toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', 'enabled', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.be.disabled', { isNot, timeout });
|
return await locator._expect('to.be.disabled', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -80,9 +78,8 @@ export function toBeEditable(
|
||||||
) {
|
) {
|
||||||
const editable = !options || options.editable === undefined || options.editable;
|
const editable = !options || options.editable === undefined || options.editable;
|
||||||
const expected = editable ? 'editable' : 'readOnly';
|
const expected = editable ? 'editable' : 'readOnly';
|
||||||
const unexpected = editable ? 'readOnly' : 'editable';
|
|
||||||
const arg = editable ? '' : '{ editable: false }';
|
const arg = editable ? '' : '{ editable: false }';
|
||||||
return toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||||
return await locator._expect(editable ? 'to.be.editable' : 'to.be.readonly', { isNot, timeout });
|
return await locator._expect(editable ? 'to.be.editable' : 'to.be.readonly', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +89,7 @@ export function toBeEmpty(
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
return toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', 'notEmpty', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.be.empty', { isNot, timeout });
|
return await locator._expect('to.be.empty', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -104,9 +101,8 @@ export function toBeEnabled(
|
||||||
) {
|
) {
|
||||||
const enabled = !options || options.enabled === undefined || options.enabled;
|
const enabled = !options || options.enabled === undefined || options.enabled;
|
||||||
const expected = enabled ? 'enabled' : 'disabled';
|
const expected = enabled ? 'enabled' : 'disabled';
|
||||||
const unexpected = enabled ? 'disabled' : 'enabled';
|
|
||||||
const arg = enabled ? '' : '{ enabled: false }';
|
const arg = enabled ? '' : '{ enabled: false }';
|
||||||
return toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||||
return await locator._expect(enabled ? 'to.be.enabled' : 'to.be.disabled', { isNot, timeout });
|
return await locator._expect(enabled ? 'to.be.enabled' : 'to.be.disabled', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +112,7 @@ export function toBeFocused(
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
return toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', 'inactive', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.be.focused', { isNot, timeout });
|
return await locator._expect('to.be.focused', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +122,7 @@ export function toBeHidden(
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
return toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', 'visible', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.be.hidden', { isNot, timeout });
|
return await locator._expect('to.be.hidden', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -138,9 +134,8 @@ export function toBeVisible(
|
||||||
) {
|
) {
|
||||||
const visible = !options || options.visible === undefined || options.visible;
|
const visible = !options || options.visible === undefined || options.visible;
|
||||||
const expected = visible ? 'visible' : 'hidden';
|
const expected = visible ? 'visible' : 'hidden';
|
||||||
const unexpected = visible ? 'hidden' : 'visible';
|
|
||||||
const arg = visible ? '' : '{ visible: false }';
|
const arg = visible ? '' : '{ visible: false }';
|
||||||
return toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, arg, async (isNot, timeout) => {
|
||||||
return await locator._expect(visible ? 'to.be.visible' : 'to.be.hidden', { isNot, timeout });
|
return await locator._expect(visible ? 'to.be.visible' : 'to.be.hidden', { isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +145,7 @@ export function toBeInViewport(
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
options?: { timeout?: number, ratio?: number },
|
options?: { timeout?: number, ratio?: number },
|
||||||
) {
|
) {
|
||||||
return toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', 'outside viewport', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.be.in.viewport', { isNot, expectedNumber: options?.ratio, timeout });
|
return await locator._expect('to.be.in.viewport', { isNot, expectedNumber: options?.ratio, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +227,7 @@ export function toHaveAttribute(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (expected === undefined) {
|
if (expected === undefined) {
|
||||||
return toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', 'not have attribute', '', async (isNot, timeout) => {
|
return toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', '', async (isNot, timeout) => {
|
||||||
return await locator._expect('to.have.attribute', { expressionArg: name, isNot, timeout });
|
return await locator._expect('to.have.attribute', { expressionArg: name, isNot, timeout });
|
||||||
}, options);
|
}, options);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export async function toBeTruthy(
|
||||||
receiver: Locator,
|
receiver: Locator,
|
||||||
receiverType: string,
|
receiverType: string,
|
||||||
expected: string,
|
expected: string,
|
||||||
unexpected: string,
|
|
||||||
arg: string,
|
arg: string,
|
||||||
query: (isNot: boolean, timeout: number) => Promise<{ matches: boolean, log?: string[], received?: any, timedOut?: boolean }>,
|
query: (isNot: boolean, timeout: number) => Promise<{ matches: boolean, log?: string[], received?: any, timedOut?: boolean }>,
|
||||||
options: { timeout?: number } = {},
|
options: { timeout?: number } = {},
|
||||||
|
|
@ -50,7 +49,6 @@ export async function toBeTruthy(
|
||||||
}
|
}
|
||||||
|
|
||||||
const notFound = received === kNoElementsFoundError ? received : undefined;
|
const notFound = received === kNoElementsFoundError ? received : undefined;
|
||||||
const actual = pass ? expected : unexpected;
|
|
||||||
let printedReceived: string | undefined;
|
let printedReceived: string | undefined;
|
||||||
let printedExpected: string | undefined;
|
let printedExpected: string | undefined;
|
||||||
if (pass) {
|
if (pass) {
|
||||||
|
|
@ -58,7 +56,7 @@ export async function toBeTruthy(
|
||||||
printedReceived = `Received: ${notFound ? kNoElementsFoundError : expected}`;
|
printedReceived = `Received: ${notFound ? kNoElementsFoundError : expected}`;
|
||||||
} else {
|
} else {
|
||||||
printedExpected = `Expected: ${expected}`;
|
printedExpected = `Expected: ${expected}`;
|
||||||
printedReceived = `Received: ${notFound ? kNoElementsFoundError : unexpected}`;
|
printedReceived = `Received: ${notFound ? kNoElementsFoundError : received}`;
|
||||||
}
|
}
|
||||||
const message = () => {
|
const message = () => {
|
||||||
const header = matcherHint(this, receiver, matcherName, 'locator', arg, matcherOptions, timedOut ? timeout : undefined);
|
const header = matcherHint(this, receiver, matcherName, 'locator', arg, matcherOptions, timedOut ? timeout : undefined);
|
||||||
|
|
@ -68,7 +66,7 @@ export async function toBeTruthy(
|
||||||
return {
|
return {
|
||||||
message,
|
message,
|
||||||
pass,
|
pass,
|
||||||
actual,
|
actual: received,
|
||||||
name: matcherName,
|
name: matcherName,
|
||||||
expected,
|
expected,
|
||||||
log,
|
log,
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ test('toBeTruthy-based assertions should have matcher result', async ({ page })
|
||||||
const e = await expect(page.locator('#node2')).toBeVisible({ timeout: 1 }).catch(e => e);
|
const e = await expect(page.locator('#node2')).toBeVisible({ timeout: 1 }).catch(e => e);
|
||||||
e.matcherResult.message = stripAnsi(e.matcherResult.message);
|
e.matcherResult.message = stripAnsi(e.matcherResult.message);
|
||||||
expect.soft(e.matcherResult).toEqual({
|
expect.soft(e.matcherResult).toEqual({
|
||||||
actual: 'hidden',
|
actual: '<element(s) not found>',
|
||||||
expected: 'visible',
|
expected: 'visible',
|
||||||
message: expect.stringContaining(`Timed out 1ms waiting for expect(locator).toBeVisible()`),
|
message: expect.stringContaining(`Timed out 1ms waiting for expect(locator).toBeVisible()`),
|
||||||
name: 'toBeVisible',
|
name: 'toBeVisible',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue