mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	api: add option position to check/uncheck (#6153)
				
					
				
			Since check/uncheck does click under the hood, sometimes it might need to click at a different position. One example would be a long label that contains links inside, and clicking in the center happens to hit the link instead of the label itself.
This commit is contained in:
		
							parent
							
								
									96cee43861
								
							
						
					
					
						commit
						e81a3c5901
					
				@ -134,6 +134,8 @@ When all steps combined have not finished during the specified [`option: timeout
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.check.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.check.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.check.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## async method: ElementHandle.click
 | 
			
		||||
@ -782,6 +784,8 @@ When all steps combined have not finished during the specified [`option: timeout
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.uncheck.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.uncheck.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: ElementHandle.uncheck.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## async method: ElementHandle.waitForElementState
 | 
			
		||||
 | 
			
		||||
@ -173,6 +173,8 @@ When all steps combined have not finished during the specified [`option: timeout
 | 
			
		||||
 | 
			
		||||
### option: Frame.check.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: Frame.check.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: Frame.check.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## method: Frame.childFrames
 | 
			
		||||
@ -1102,6 +1104,8 @@ When all steps combined have not finished during the specified [`option: timeout
 | 
			
		||||
 | 
			
		||||
### option: Frame.uncheck.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: Frame.uncheck.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: Frame.uncheck.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## method: Frame.url
 | 
			
		||||
 | 
			
		||||
@ -531,6 +531,8 @@ Shortcut for main frame's [`method: Frame.check`].
 | 
			
		||||
 | 
			
		||||
### option: Page.check.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: Page.check.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: Page.check.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## async method: Page.click
 | 
			
		||||
@ -2505,6 +2507,8 @@ Shortcut for main frame's [`method: Frame.uncheck`].
 | 
			
		||||
 | 
			
		||||
### option: Page.uncheck.noWaitAfter = %%-input-no-wait-after-%%
 | 
			
		||||
 | 
			
		||||
### option: Page.uncheck.position = %%-input-position-%%
 | 
			
		||||
 | 
			
		||||
### option: Page.uncheck.timeout = %%-input-timeout-%%
 | 
			
		||||
 | 
			
		||||
## async method: Page.unroute
 | 
			
		||||
 | 
			
		||||
@ -1336,11 +1336,13 @@ export type FrameCheckParams = {
 | 
			
		||||
  selector: string,
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type FrameCheckOptions = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type FrameCheckResult = void;
 | 
			
		||||
@ -1698,11 +1700,13 @@ export type FrameUncheckParams = {
 | 
			
		||||
  selector: string,
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type FrameUncheckOptions = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type FrameUncheckResult = void;
 | 
			
		||||
@ -1902,11 +1906,13 @@ export type ElementHandleBoundingBoxResult = {
 | 
			
		||||
export type ElementHandleCheckParams = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type ElementHandleCheckOptions = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type ElementHandleCheckResult = void;
 | 
			
		||||
@ -2174,11 +2180,13 @@ export type ElementHandleTypeResult = void;
 | 
			
		||||
export type ElementHandleUncheckParams = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type ElementHandleUncheckOptions = {
 | 
			
		||||
  force?: boolean,
 | 
			
		||||
  noWaitAfter?: boolean,
 | 
			
		||||
  position?: Point,
 | 
			
		||||
  timeout?: number,
 | 
			
		||||
};
 | 
			
		||||
export type ElementHandleUncheckResult = void;
 | 
			
		||||
 | 
			
		||||
@ -1045,6 +1045,7 @@ Frame:
 | 
			
		||||
        selector: string
 | 
			
		||||
        force: boolean?
 | 
			
		||||
        noWaitAfter: boolean?
 | 
			
		||||
        position: Point?
 | 
			
		||||
        timeout: number?
 | 
			
		||||
 | 
			
		||||
    click:
 | 
			
		||||
@ -1352,6 +1353,7 @@ Frame:
 | 
			
		||||
        selector: string
 | 
			
		||||
        force: boolean?
 | 
			
		||||
        noWaitAfter: boolean?
 | 
			
		||||
        position: Point?
 | 
			
		||||
        timeout: number?
 | 
			
		||||
 | 
			
		||||
    waitForFunction:
 | 
			
		||||
@ -1524,6 +1526,7 @@ ElementHandle:
 | 
			
		||||
      parameters:
 | 
			
		||||
        force: boolean?
 | 
			
		||||
        noWaitAfter: boolean?
 | 
			
		||||
        position: Point?
 | 
			
		||||
        timeout: number?
 | 
			
		||||
 | 
			
		||||
    click:
 | 
			
		||||
@ -1753,6 +1756,7 @@ ElementHandle:
 | 
			
		||||
      parameters:
 | 
			
		||||
        force: boolean?
 | 
			
		||||
        noWaitAfter: boolean?
 | 
			
		||||
        position: Point?
 | 
			
		||||
        timeout: number?
 | 
			
		||||
 | 
			
		||||
    waitForElementState:
 | 
			
		||||
 | 
			
		||||
@ -539,6 +539,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
 | 
			
		||||
    selector: tString,
 | 
			
		||||
    force: tOptional(tBoolean),
 | 
			
		||||
    noWaitAfter: tOptional(tBoolean),
 | 
			
		||||
    position: tOptional(tType('Point')),
 | 
			
		||||
    timeout: tOptional(tNumber),
 | 
			
		||||
  });
 | 
			
		||||
  scheme.FrameClickParams = tObject({
 | 
			
		||||
@ -705,6 +706,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
 | 
			
		||||
    selector: tString,
 | 
			
		||||
    force: tOptional(tBoolean),
 | 
			
		||||
    noWaitAfter: tOptional(tBoolean),
 | 
			
		||||
    position: tOptional(tType('Point')),
 | 
			
		||||
    timeout: tOptional(tNumber),
 | 
			
		||||
  });
 | 
			
		||||
  scheme.FrameWaitForFunctionParams = tObject({
 | 
			
		||||
@ -767,6 +769,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
 | 
			
		||||
  scheme.ElementHandleCheckParams = tObject({
 | 
			
		||||
    force: tOptional(tBoolean),
 | 
			
		||||
    noWaitAfter: tOptional(tBoolean),
 | 
			
		||||
    position: tOptional(tType('Point')),
 | 
			
		||||
    timeout: tOptional(tNumber),
 | 
			
		||||
  });
 | 
			
		||||
  scheme.ElementHandleClickParams = tObject({
 | 
			
		||||
@ -877,6 +880,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
 | 
			
		||||
  scheme.ElementHandleUncheckParams = tObject({
 | 
			
		||||
    force: tOptional(tBoolean),
 | 
			
		||||
    noWaitAfter: tOptional(tBoolean),
 | 
			
		||||
    position: tOptional(tType('Point')),
 | 
			
		||||
    timeout: tOptional(tNumber),
 | 
			
		||||
  });
 | 
			
		||||
  scheme.ElementHandleWaitForElementStateParams = tObject({
 | 
			
		||||
 | 
			
		||||
@ -608,7 +608,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }, 'input');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async check(metadata: CallMetadata, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
  async check(metadata: CallMetadata, options: { position?: types.Point } & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const controller = new ProgressController(metadata, this);
 | 
			
		||||
    return controller.run(async progress => {
 | 
			
		||||
      const result = await this._setChecked(progress, true, options);
 | 
			
		||||
@ -616,7 +616,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }, this._page._timeoutSettings.timeout(options));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async uncheck(metadata: CallMetadata, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
  async uncheck(metadata: CallMetadata, options: { position?: types.Point } & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions) {
 | 
			
		||||
    const controller = new ProgressController(metadata, this);
 | 
			
		||||
    return controller.run(async progress => {
 | 
			
		||||
      const result = await this._setChecked(progress, false, options);
 | 
			
		||||
@ -624,7 +624,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
 | 
			
		||||
    }, this._page._timeoutSettings.timeout(options));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async _setChecked(progress: Progress, state: boolean, options: types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
 | 
			
		||||
  async _setChecked(progress: Progress, state: boolean, options: { position?: types.Point } & types.PointerActionWaitOptions & types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
 | 
			
		||||
    const isChecked = async () => {
 | 
			
		||||
      const result = await this.evaluateInUtility(([injected, node]) => injected.checkElementState(node, 'checked'), {});
 | 
			
		||||
      return throwRetargetableDOMError(throwFatalDOMError(result));
 | 
			
		||||
 | 
			
		||||
@ -107,3 +107,15 @@ it('should check the box inside a button', async ({page}) => {
 | 
			
		||||
  expect(await page.isChecked('input')).toBe(true);
 | 
			
		||||
  expect(await (await page.$('input')).isChecked()).toBe(true);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('should check the label with position', async ({page, server}) => {
 | 
			
		||||
  await page.setContent(`
 | 
			
		||||
    <input id='checkbox' type='checkbox' style='width: 5px; height: 5px;'>
 | 
			
		||||
    <label for='checkbox'>
 | 
			
		||||
      <a href=${JSON.stringify(server.EMPTY_PAGE)}>I am a long link that goes away so that nothing good will happen if you click on me</a>
 | 
			
		||||
      Click me
 | 
			
		||||
    </label>`);
 | 
			
		||||
  const box = await (await page.$('text=Click me')).boundingBox();
 | 
			
		||||
  await page.check('text=Click me', { position: { x: box.width - 10, y: 2 } });
 | 
			
		||||
  expect(await page.$eval('input', input => input.checked)).toBe(true);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								types/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								types/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -1375,6 +1375,16 @@ export interface Page {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
@ -2812,6 +2822,16 @@ export interface Page {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
@ -3567,6 +3587,16 @@ export interface Frame {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
@ -4420,6 +4450,16 @@ export interface Frame {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
@ -5721,6 +5761,16 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
@ -6405,6 +6455,16 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
 | 
			
		||||
     */
 | 
			
		||||
    noWaitAfter?: boolean;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A point to use relative to the top-left corner of element padding box. If not specified, uses some visible point of the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    position?: {
 | 
			
		||||
      x: number;
 | 
			
		||||
 | 
			
		||||
      y: number;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by
 | 
			
		||||
     * using the
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user