feat(toBeChecked): allow passing checked: false (#10665)
This commit is contained in:
parent
2ac9c08d0c
commit
31e0a63fcd
|
|
@ -280,8 +280,11 @@ locator = page.locator(".subscribe")
|
|||
expect(locator).to_be_checked()
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toBeChecked.timeout = %%-assertions-timeout-%%
|
||||
### option: LocatorAssertions.toBeChecked.checked
|
||||
* langs: js
|
||||
- `checked` <[boolean]>
|
||||
|
||||
### option: LocatorAssertions.toBeChecked.timeout = %%-assertions-timeout-%%
|
||||
|
||||
|
||||
## method: LocatorAssertions.toBeDisabled
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ export class Locator implements api.Locator {
|
|||
await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options });
|
||||
}
|
||||
|
||||
async _expect(expression: string, options: FrameExpectOptions): Promise<{ matches: boolean, received?: any, log?: string[] }> {
|
||||
async _expect(expression: string, options: Omit<FrameExpectOptions, 'expectedValue'> & { expectedValue?: any }): Promise<{ matches: boolean, received?: any, log?: string[] }> {
|
||||
const params: channels.FrameExpectParams = { selector: this._selector, expression, ...options, isNot: !!options.isNot };
|
||||
if (options.expectedValue)
|
||||
params.expectedValue = serializeArgument(options.expectedValue);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export type InjectedScriptPoll<T> = {
|
|||
cancel: () => void,
|
||||
};
|
||||
|
||||
export type ElementStateWithoutStable = 'visible' | 'hidden' | 'enabled' | 'disabled' | 'editable' | 'checked';
|
||||
export type ElementStateWithoutStable = 'visible' | 'hidden' | 'enabled' | 'disabled' | 'editable' | 'checked' | 'unchecked';
|
||||
export type ElementState = ElementStateWithoutStable | 'stable';
|
||||
|
||||
export interface SelectorEngineV2 {
|
||||
|
|
@ -502,14 +502,17 @@ export class InjectedScript {
|
|||
if (state === 'editable')
|
||||
return !disabled && editable;
|
||||
|
||||
if (state === 'checked') {
|
||||
if (['checkbox', 'radio'].includes(element.getAttribute('role') || ''))
|
||||
return element.getAttribute('aria-checked') === 'true';
|
||||
if (state === 'checked' || state === 'unchecked') {
|
||||
if (['checkbox', 'radio'].includes(element.getAttribute('role') || '')) {
|
||||
const result = element.getAttribute('aria-checked') === 'true';
|
||||
return state === 'checked' ? result : !result;
|
||||
}
|
||||
if (element.nodeName !== 'INPUT')
|
||||
throw this.createStacklessError('Not a checkbox or radio button');
|
||||
if (!['radio', 'checkbox'].includes((element as HTMLInputElement).type.toLowerCase()))
|
||||
throw this.createStacklessError('Not a checkbox or radio button');
|
||||
return (element as HTMLInputElement).checked;
|
||||
const result = (element as HTMLInputElement).checked;
|
||||
return state === 'checked' ? result : !result;
|
||||
}
|
||||
throw this.createStacklessError(`Unexpected element state "${state}"`);
|
||||
}
|
||||
|
|
@ -899,6 +902,8 @@ export class InjectedScript {
|
|||
let elementState: boolean | 'error:notconnected' | 'error:notcheckbox' | undefined;
|
||||
if (expression === 'to.be.checked') {
|
||||
elementState = progress.injectedScript.elementState(element, 'checked');
|
||||
} else if (expression === 'to.be.unchecked') {
|
||||
elementState = progress.injectedScript.elementState(element, 'unchecked');
|
||||
} else if (expression === 'to.be.disabled') {
|
||||
elementState = progress.injectedScript.elementState(element, 'disabled');
|
||||
} else if (expression === 'to.be.editable') {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { toEqual } from './toEqual';
|
|||
import { callLogText, toExpectedTextValues, toMatchText } from './toMatchText';
|
||||
|
||||
interface LocatorEx extends Locator {
|
||||
_expect(expression: string, options: FrameExpectOptions): Promise<{ matches: boolean, received?: any, log?: string[] }>;
|
||||
_expect(expression: string, options: Omit<FrameExpectOptions, 'expectedValue'> & { expectedValue?: any }): Promise<{ matches: boolean, received?: any, log?: string[] }>;
|
||||
}
|
||||
|
||||
interface APIResponseEx extends APIResponse {
|
||||
|
|
@ -34,10 +34,11 @@ interface APIResponseEx extends APIResponse {
|
|||
export function toBeChecked(
|
||||
this: ReturnType<Expect['getState']>,
|
||||
locator: LocatorEx,
|
||||
options?: { timeout?: number },
|
||||
options?: { checked?: boolean, timeout?: number },
|
||||
) {
|
||||
return toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', async (isNot, timeout) => {
|
||||
return await locator._expect('to.be.checked', { isNot, timeout });
|
||||
const checked = !options || options.checked === undefined || options.checked === true;
|
||||
return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', { isNot, timeout });
|
||||
}, options);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type * as expect from 'expect';
|
||||
|
|
@ -69,9 +77,9 @@ declare global {
|
|||
}): R;
|
||||
|
||||
/**
|
||||
* Asserts input is checked.
|
||||
* Asserts input is checked (or unchecked if { checked: false } is passed).
|
||||
*/
|
||||
toBeChecked(options?: { timeout?: number }): Promise<R>;
|
||||
toBeChecked(options?: { checked?: boolean, timeout?: number }): Promise<R>;
|
||||
|
||||
/**
|
||||
* Asserts input is disabled.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,18 @@ test('should support toBeChecked', async ({ runInlineTest }) => {
|
|||
await expect(locator).toBeChecked();
|
||||
});
|
||||
|
||||
test('pass 2', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox checked></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).toBeChecked({ checked: true });
|
||||
});
|
||||
|
||||
test('pass 3', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox checked></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).not.toBeChecked({ checked: false });
|
||||
});
|
||||
|
||||
test('fail', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox></input>');
|
||||
const locator = page.locator('input');
|
||||
|
|
@ -37,7 +49,7 @@ test('should support toBeChecked', async ({ runInlineTest }) => {
|
|||
const output = stripAscii(result.output);
|
||||
expect(output).toContain('Error: expect(received).toBeChecked()');
|
||||
expect(output).toContain('expect(locator).toBeChecked');
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.passed).toBe(3);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
|
@ -53,22 +65,34 @@ test('should support toBeChecked w/ not', async ({ runInlineTest }) => {
|
|||
await expect(locator).not.toBeChecked();
|
||||
});
|
||||
|
||||
test('pass 2', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).toBeChecked({ checked: false });
|
||||
});
|
||||
|
||||
test('fail not', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox checked></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).not.toBeChecked({ timeout: 1000 });
|
||||
await expect(locator).not.toBeChecked({ timeout: 500 });
|
||||
});
|
||||
|
||||
test('fail 2', async ({ page }) => {
|
||||
await page.setContent('<input type=checkbox checked></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).toBeChecked({ checked: false, timeout: 500 });
|
||||
});
|
||||
|
||||
test('fail missing', async ({ page }) => {
|
||||
await page.setContent('<div>no inputs here</div>');
|
||||
const locator2 = page.locator('input2');
|
||||
await expect(locator2).not.toBeChecked({ timeout: 1000 });
|
||||
await expect(locator2).not.toBeChecked({ timeout: 500 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.failed).toBe(2);
|
||||
expect(result.passed).toBe(2);
|
||||
expect(result.failed).toBe(3);
|
||||
expect(result.exitCode).toBe(1);
|
||||
// fail not
|
||||
expect(output).toContain('Error: expect(received).not.toBeChecked()');
|
||||
|
|
|
|||
Loading…
Reference in New Issue