mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	feat: locator.visible (#34905)
This commit is contained in:
		
							parent
							
								
									9b633ddd2f
								
							
						
					
					
						commit
						ed0bf35435
					
				@ -2478,6 +2478,18 @@ When all steps combined have not finished during the specified [`option: timeout
 | 
			
		||||
### option: Locator.uncheck.trial = %%-input-trial-%%
 | 
			
		||||
* since: v1.14
 | 
			
		||||
 | 
			
		||||
## method: Locator.visible
 | 
			
		||||
* since: v1.51
 | 
			
		||||
- returns: <[Locator]>
 | 
			
		||||
 | 
			
		||||
Returns a locator that only matches [visible](../actionability.md#visible) elements.
 | 
			
		||||
 | 
			
		||||
### option: Locator.visible.visible
 | 
			
		||||
* since: v1.51
 | 
			
		||||
- `visible` <[boolean]>
 | 
			
		||||
 | 
			
		||||
Whether to match visible or invisible elements.
 | 
			
		||||
 | 
			
		||||
## async method: Locator.waitFor
 | 
			
		||||
* since: v1.16
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1310,19 +1310,19 @@ Consider a page with two buttons, the first invisible and the second [visible](.
 | 
			
		||||
* This will only find a second button, because it is visible, and then click it.
 | 
			
		||||
 | 
			
		||||
  ```js
 | 
			
		||||
  await page.locator('button').locator('visible=true').click();
 | 
			
		||||
  await page.locator('button').visible().click();
 | 
			
		||||
  ```
 | 
			
		||||
  ```java
 | 
			
		||||
  page.locator("button").locator("visible=true").click();
 | 
			
		||||
  page.locator("button").visible().click();
 | 
			
		||||
  ```
 | 
			
		||||
  ```python async
 | 
			
		||||
  await page.locator("button").locator("visible=true").click()
 | 
			
		||||
  await page.locator("button").visible().click()
 | 
			
		||||
  ```
 | 
			
		||||
  ```python sync
 | 
			
		||||
  page.locator("button").locator("visible=true").click()
 | 
			
		||||
  page.locator("button").visible().click()
 | 
			
		||||
  ```
 | 
			
		||||
  ```csharp
 | 
			
		||||
  await page.Locator("button").Locator("visible=true").ClickAsync();
 | 
			
		||||
  await page.Locator("button").Visible().ClickAsync();
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
## Lists
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/playwright-client/types/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								packages/playwright-client/types/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -14615,6 +14615,17 @@ export interface Locator {
 | 
			
		||||
    trial?: boolean;
 | 
			
		||||
  }): Promise<void>;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns a locator that only matches [visible](https://playwright.dev/docs/actionability#visible) elements.
 | 
			
		||||
   * @param options
 | 
			
		||||
   */
 | 
			
		||||
  visible(options?: {
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to match visible or invisible elements.
 | 
			
		||||
     */
 | 
			
		||||
    visible?: boolean;
 | 
			
		||||
  }): Locator;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns when element specified by locator satisfies the
 | 
			
		||||
   * [`state`](https://playwright.dev/docs/api/class-locator#locator-wait-for-option-state) option.
 | 
			
		||||
 | 
			
		||||
@ -218,6 +218,11 @@ export class Locator implements api.Locator {
 | 
			
		||||
    return new Locator(this._frame, this._selector + ` >> nth=${index}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  visible(options: { visible?: boolean } = {}): Locator {
 | 
			
		||||
    const { visible = true } = options;
 | 
			
		||||
    return new Locator(this._frame, this._selector + ` >> visible=${visible ? 'true' : 'false'}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  and(locator: Locator): Locator {
 | 
			
		||||
    if (locator._frame !== this._frame)
 | 
			
		||||
      throw new Error(`Locators must belong to the same frame.`);
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import type { NestedSelectorBody } from './selectorParser';
 | 
			
		||||
import type { ParsedSelector } from './selectorParser';
 | 
			
		||||
 | 
			
		||||
export type Language = 'javascript' | 'python' | 'java' | 'csharp' | 'jsonl';
 | 
			
		||||
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'has-text' | 'has-not-text' | 'has' | 'hasNot' | 'frame' | 'frame-locator' | 'and' | 'or' | 'chain';
 | 
			
		||||
export type LocatorType = 'default' | 'role' | 'text' | 'label' | 'placeholder' | 'alt' | 'title' | 'test-id' | 'nth' | 'first' | 'last' | 'visible' | 'has-text' | 'has-not-text' | 'has' | 'hasNot' | 'frame' | 'frame-locator' | 'and' | 'or' | 'chain';
 | 
			
		||||
export type LocatorBase = 'page' | 'locator' | 'frame-locator';
 | 
			
		||||
export type Quote = '\'' | '"' | '`';
 | 
			
		||||
 | 
			
		||||
@ -68,6 +68,10 @@ function innerAsLocators(factory: LocatorFactory, parsed: ParsedSelector, isFram
 | 
			
		||||
        tokens.push([factory.generateLocator(base, 'nth', part.body as string)]);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (part.name === 'visible') {
 | 
			
		||||
      tokens.push([factory.generateLocator(base, 'visible', part.body as string), factory.generateLocator(base, 'default', `visible=${part.body}`)]);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (part.name === 'internal:text') {
 | 
			
		||||
      const { exact, text } = detectExact(part.body as string);
 | 
			
		||||
      tokens.push([factory.generateLocator(base, 'text', text, { exact })]);
 | 
			
		||||
@ -275,6 +279,8 @@ export class JavaScriptLocatorFactory implements LocatorFactory {
 | 
			
		||||
        return `first()`;
 | 
			
		||||
      case 'last':
 | 
			
		||||
        return `last()`;
 | 
			
		||||
      case 'visible':
 | 
			
		||||
        return `visible(${body === 'true' ? '' : '{ visible: false }'})`;
 | 
			
		||||
      case 'role':
 | 
			
		||||
        const attrs: string[] = [];
 | 
			
		||||
        if (isRegExp(options.name)) {
 | 
			
		||||
@ -369,6 +375,8 @@ export class PythonLocatorFactory implements LocatorFactory {
 | 
			
		||||
        return `first`;
 | 
			
		||||
      case 'last':
 | 
			
		||||
        return `last`;
 | 
			
		||||
      case 'visible':
 | 
			
		||||
        return `visible(${body === 'true' ? '' : 'visible=False'})`;
 | 
			
		||||
      case 'role':
 | 
			
		||||
        const attrs: string[] = [];
 | 
			
		||||
        if (isRegExp(options.name)) {
 | 
			
		||||
@ -476,6 +484,8 @@ export class JavaLocatorFactory implements LocatorFactory {
 | 
			
		||||
        return `first()`;
 | 
			
		||||
      case 'last':
 | 
			
		||||
        return `last()`;
 | 
			
		||||
      case 'visible':
 | 
			
		||||
        return `visible(${body === 'true' ? '' : `new ${clazz}.VisibleOptions().setVisible(false)`})`;
 | 
			
		||||
      case 'role':
 | 
			
		||||
        const attrs: string[] = [];
 | 
			
		||||
        if (isRegExp(options.name)) {
 | 
			
		||||
@ -573,6 +583,8 @@ export class CSharpLocatorFactory implements LocatorFactory {
 | 
			
		||||
        return `First`;
 | 
			
		||||
      case 'last':
 | 
			
		||||
        return `Last`;
 | 
			
		||||
      case 'visible':
 | 
			
		||||
        return `Visible(${body === 'true' ? '' : 'new() { Visible = false }'})`;
 | 
			
		||||
      case 'role':
 | 
			
		||||
        const attrs: string[] = [];
 | 
			
		||||
        if (isRegExp(options.name)) {
 | 
			
		||||
 | 
			
		||||
@ -170,6 +170,9 @@ function transform(template: string, params: TemplateParams, testIdAttributeName
 | 
			
		||||
      .replace(/first(\(\))?/g, 'nth=0')
 | 
			
		||||
      .replace(/last(\(\))?/g, 'nth=-1')
 | 
			
		||||
      .replace(/nth\(([^)]+)\)/g, 'nth=$1')
 | 
			
		||||
      .replace(/visible\(,?visible=true\)/g, 'visible=true')
 | 
			
		||||
      .replace(/visible\(,?visible=false\)/g, 'visible=false')
 | 
			
		||||
      .replace(/visible\(\)/g, 'visible=true')
 | 
			
		||||
      .replace(/filter\(,?hastext=([^)]+)\)/g, 'internal:has-text=$1')
 | 
			
		||||
      .replace(/filter\(,?hasnottext=([^)]+)\)/g, 'internal:has-not-text=$1')
 | 
			
		||||
      .replace(/filter\(,?has2=([^)]+)\)/g, 'internal:has=$1')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/playwright-core/types/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								packages/playwright-core/types/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -14615,6 +14615,17 @@ export interface Locator {
 | 
			
		||||
    trial?: boolean;
 | 
			
		||||
  }): Promise<void>;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns a locator that only matches [visible](https://playwright.dev/docs/actionability#visible) elements.
 | 
			
		||||
   * @param options
 | 
			
		||||
   */
 | 
			
		||||
  visible(options?: {
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to match visible or invisible elements.
 | 
			
		||||
     */
 | 
			
		||||
    visible?: boolean;
 | 
			
		||||
  }): Locator;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns when element specified by locator satisfies the
 | 
			
		||||
   * [`state`](https://playwright.dev/docs/api/class-locator#locator-wait-for-option-state) option.
 | 
			
		||||
 | 
			
		||||
@ -320,6 +320,27 @@ it('reverse engineer hasNotText', async ({ page }) => {
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('reverse engineer visible', async ({ page }) => {
 | 
			
		||||
  expect.soft(generate(page.getByText('Hello').visible().locator('div'))).toEqual({
 | 
			
		||||
    csharp: `GetByText("Hello").Visible().Locator("div")`,
 | 
			
		||||
    java: `getByText("Hello").visible().locator("div")`,
 | 
			
		||||
    javascript: `getByText('Hello').visible().locator('div')`,
 | 
			
		||||
    python: `get_by_text("Hello").visible().locator("div")`,
 | 
			
		||||
  });
 | 
			
		||||
  expect.soft(generate(page.getByText('Hello').visible({ visible: true }).locator('div'))).toEqual({
 | 
			
		||||
    csharp: `GetByText("Hello").Visible().Locator("div")`,
 | 
			
		||||
    java: `getByText("Hello").visible().locator("div")`,
 | 
			
		||||
    javascript: `getByText('Hello').visible().locator('div')`,
 | 
			
		||||
    python: `get_by_text("Hello").visible().locator("div")`,
 | 
			
		||||
  });
 | 
			
		||||
  expect.soft(generate(page.getByText('Hello').visible({ visible: false }).locator('div'))).toEqual({
 | 
			
		||||
    csharp: `GetByText("Hello").Visible(new() { Visible = false }).Locator("div")`,
 | 
			
		||||
    java: `getByText("Hello").visible(new Locator.VisibleOptions().setVisible(false)).locator("div")`,
 | 
			
		||||
    javascript: `getByText('Hello').visible({ visible: false }).locator('div')`,
 | 
			
		||||
    python: `get_by_text("Hello").visible(visible=False).locator("div")`,
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('reverse engineer has', async ({ page }) => {
 | 
			
		||||
  expect.soft(generate(page.getByText('Hello').filter({ has: page.locator('div').getByText('bye') }))).toEqual({
 | 
			
		||||
    csharp: `GetByText("Hello").Filter(new() { Has = Locator("div").GetByText("bye") })`,
 | 
			
		||||
 | 
			
		||||
@ -150,6 +150,23 @@ it('should combine visible with other selectors', async ({ page }) => {
 | 
			
		||||
  await expect(page.locator('.item >> visible=true >> text=data3')).toHaveText('visible data3');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('should support .visible()', async ({ page }) => {
 | 
			
		||||
  await page.setContent(`<div>
 | 
			
		||||
    <div class="item" style="display: none">Hidden data0</div>
 | 
			
		||||
    <div class="item">visible data1</div>
 | 
			
		||||
    <div class="item" style="display: none">Hidden data1</div>
 | 
			
		||||
    <div class="item">visible data2</div>
 | 
			
		||||
    <div class="item" style="display: none">Hidden data2</div>
 | 
			
		||||
    <div class="item">visible data3</div>
 | 
			
		||||
    </div>
 | 
			
		||||
  `);
 | 
			
		||||
  const locator = page.locator('.item').visible().nth(1);
 | 
			
		||||
  await expect(locator).toHaveText('visible data2');
 | 
			
		||||
  await expect(page.locator('.item').visible().getByText('data3')).toHaveText('visible data3');
 | 
			
		||||
  await expect(page.locator('.item').visible({ visible: true }).getByText('data2')).toHaveText('visible data2');
 | 
			
		||||
  await expect(page.locator('.item').visible({ visible: false }).getByText('data1')).toHaveText('Hidden data1');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('locator.count should work with deleted Map in main world', async ({ page }) => {
 | 
			
		||||
  it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11254' });
 | 
			
		||||
  await page.evaluate('Map = 1');
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user