Skip to content

[Feature] Acces to the locator selector #10178

Closed

Description

Let us know what functionality you'd like to see in Playwright and what your use case is.
Do you think others might benefit from this as well?

Hey,

we would like to have access to the selector of our locator. Thanks to that solution we can create methods which can enhance/modify our base selector.

Example
image

and then we can implement such thing:

class Header extends PageObjectComponent {
    $ = {
        button: this.page.locator('[e2e-target=button]'),
    }
    constructor(page) {
        super(page, page.locator('[e2e-target="header"]'))
    }

    async hoverOnHeader(): Promise<void> {
        await this.parentElement.hover()
    }
}

class LandingPage extends PageObject {
    $ = {
        footer: new Footer(this.page),
        header: new Header(this.page),
    }

    constructor(page) {
        super(page, landingPageUrl)
    }

    async waitForPageLoad(): Promise<void> {
        await this.$.header.hoverOnHeader()
    }
    
    async clickConfirmButton Promise(): void {
        const baseSelector: string = this.$.header.$.button.selector
        await this.page.locator(`${baseSelector}[e2e-target-name="confirm"]`).click()
    }
}

Activity

  1. pavelfeldman commented on Nov 9, 2021

    @pavelfeldman
    Member

    Looks like if we address #10177, you wouldn't need this one, right?

  2. Evilweed commented on Nov 9, 2021

    @Evilweed

    Pavel Feldman (@pavelfeldman) i think it would be still useful to be able to create selectors that cannot be created with .withAttribute()

    For example to create equivalents of:
    withText()
    withExactText()

    Seems like .withAttribute() is easier to use, but this seems to be more powerful:

    const baseSelector: string = this.$.header.$.button.selector
    await this.page.locator(`${baseSelector}[e2e-target-name="confirm"]:contains("some text")`).click()
    
  3. pavelfeldman commented on Nov 10, 2021

    @pavelfeldman
    Member

    Selector is an implementation detail of the locator, we can't create locator from selector in the general case. But we can try amending conditions like you attribute. I'll close this one in favor of withAttribute. We already have text and exact text matching via adding .locator('text=Hello').

  4. Evilweed commented on Nov 10, 2021

    @Evilweed

    Pavel Feldman (@pavelfeldman) if you are implementing only withAttribute() then i need to open new feature requests for withText() and withExactText() (or withText with regexp support).

    This is because withAttribute() and your suggested .locator('text=Hello') does not solve my problem entirely :)

    withAttribute() already exists too, it is .locator([attribute-name="someValue"]) so in theory anyone could use this, but what we need is to be able to extend current locator instead of searching for new thing within parent locator.

    So much easier to create DRY code like that:

    $ = {
      countries: this.page.locator('option')
      disabledCountries: this.$.countries.withAttribute('e2e-which', 'disabled')
      country: (name: string | Regexp) => this.$.countries.withText(name)
      disabledCountry: (name: string | Regexp) => this.$.disabledCountries.withText(name)
    }
    

    Than this:

    const countryOptionCssSelector = 'option'
    const disabledCountryOptionCssSelector = 'option'
    const pageObject1 = {
      $ = {
        countries: this.page.locator(countryOptionCssSelector)
        disabledCountries: this.page.locator(`${countryOptionCssSelector}${disabledCountryOptionCssSelector}`)
        country: (name: string | Regexp) => this.page.locator(`${countryOptionCssSelector} text=${name}`)
        disabledCountry: (name: string | Regexp) => this.page.locator(`${countryOptionCssSelector}${disabledCountryOptionCssSelector} text=${name}`)
      }
    }
    
    # Not to mention that sometimes you are passing parentElement to nested page objects, or you need to reuse/extend selector from other page object and it gets even less nice
    # It's harder to maintain code and you need to keep track where you have all of those CssSelectors defined.
    
    import countryOptionCssSelector from ...
    import disabledCountryOptionCssSelector from ...
    
    const pageObject2 = {
      $ = {
        something: this.page.locator(`${countryOptionCssSelector}${disabledCountryOptionCssSelector} ...`)
      }
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions