0

I have a function, contains the page objects

class TemplateListPage:
   def __init__(self, page):
      self.list_first_row = self.page.locator(".grid-row").first
      self.use_btn = self.page.locator(".useTemplate")

And I would like to chain the button with the first row in assertion, in the spec test. Like

expect(TemplateListPage().list_first_row.use_btn).to_have_count(0)

or

TemplateListPage().list_first_row.use_btn.click()

But got an error:

AttributeError: 'Locator' object has no attribute 'use_btn'

Is there any way that I can chain the page object locators in the test?

5
  • The way you have it set up, this isn't really possible since self.page.locator(".useTemplate") is already locating directly on the whole page, not on page.locator(".grid-row").first specifically. You can't re-chain that assignment after the fact. Sure, you could use some introspection to make this possible but, eh, I'd suggest just writing normal, readable Python code instead of trying to be too clever. Commented Oct 25, 2023 at 5:27
  • I think, this is a standard way to build class for different pages and collect the page objects for the page. With this, you will have to adapt here only if anything changed for an element. Commented Oct 25, 2023 at 8:44
  • as @ggorlen mentioned, you are addressing your button to the hole page. you can inject a locator to a locator locator(page.locator()) or you can .filter() your locators. nevertheless, you can chain locators, because first returns a locator. However you could chain like first_row = ....locator("...").first -> btn = locator(....) --> first_row.locator(btn) Commented Oct 25, 2023 at 9:56
  • TemplateListPage().list_first_row.use_btn isn't standard. There's no easy way to do that with locators. TemplateListPage().list_first_row_use_btn is much easier and more standard, but then the relationship is hardcoded. With only 2 locators, there's no way to determine whether this makes sense in your larger use case (I assume you want to reuse .use_btn on many different parent locators). Commented Oct 25, 2023 at 14:20
  • Thank you, @KlimBom unfortunately, I cannot use btn = locator(....) because page is needed (got error "Unresolved reference 'locator'" anyway). Yes, ggorlen, I would like to use the POs for all rows. The table rows could change dynamically, so there is a use_btn for all rows... When writing the script, I would use the nth as for loop param for rows and pick the related use_btn of that specific row Commented Oct 25, 2023 at 14:58

1 Answer 1

1

Playwright recently added the and_ locator, which should allow you to achieve what your aiming for:

expect(TemplateListPage().use_btn.and_(TemplateListPage().list_first_row)).to_have_count(0)
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the tip. I didn't know about the existance of the and_ locator. Why didn't the Playwright team called it and instead of and_ ?
Yes, this feature is available from v1.34, so I need to upgrade :) playwright.dev/python/docs/release-notes#version-134
@hpr and is a keyword so and_ needs to be used.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.