You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature] Make it easier to reuse locators for chaining #13672
It makes sense in my use case to have these as separate locators.
But in some instances I would essentially want to chain the parent locator to the child locator.
I could add another locator this.parentChild = page.locator('[data-id=parent] [data-id=child]') but it seems like duplication of selectors that are already defined.
Or I could extract out the selectors but that again is more code.
Some ideas are:
Allow the first arg of page.locator() to be either a selector: string OR a locator: Locator --> this.parent.locator(this.child)
Be able to get the selector from a locator --> this.parent.locator(this.child.selector)
That solves the problem if child is always under parent.
But what if in some instances child is not under parent.
Sorry, not good naming in the example.
It's alright. In that case, could you not use a string instead of a Locator object ?
So you can create your selector, and then chain it or not depending if it's under the parent, i.e:
Yes, both your proposed solutions make sense and I have considered them.
Maybe I wasn't clear enough as to why I'm requesting this feature.
It's not that there is currently anything blocking me from achieving my use case.
It's more of a convenience thing.
It would be good if you could get away with only having locators in the page object instead of having a mix of locators and string selectors. Keeps the class smaller and cleaner IMO.
Feel free to close if you don't think this is something that is worth pursuing.
classMyPageObject{page: Page;parent: Locator|undefined;constructor(page: Page,parent?: Locator){this.page=page;this.parent=parent;}$parent=this.page.locator('#sidebar');$someButtonInMenu=this.getByTestId('menuButton');$menu=this.$parent.getByTestId('menuItem');asyncopenMenu(buttonTitle: string,menuTitle?: string){if(menuTitle)constmenuLocator=this.$menu.filter({hasText: menuTitle});menuLocator.click()menuLocator.locator(this.$someButtonInMenu.filter({hasText: buttonTitle})).click();// <--- desired way to chaining here}else{this.$someButtonInMenu.filter({hasText: buttonTitle}).click();// <--- desired way to chaining here}}
I want to click on menuLocator and then on $someButtonInMenu.
It would be great if I work only with locators (define them in top of class), but I have to mix locators and selectors (strings) now
classMyPageObject{page: Page;parent: Locator|undefined;constructor(page: Page,parent?: Locator){this.page=page;this.parent=parent;}$parent: Locator=this.page.locator('#sidebar');$someButtonInMenu: string='[data-test="menuButton"]';// String and locators (((($menu: Locator=this.$parent.getByTestId('menuItem');asyncopenMenu(buttonTitle: string,menuTitle?: string){if(menuTitle){constmenuLocator=this.$menu.filter({hasText: menuTitle});menuLocator.click();menuLocator.locator(this.$someButtonInMenu,{hasText: buttonTitle}).click();// Different ways to work with elements on page}elsethis.page.locator(this.$someButtonInMenu,{hasText: buttonTitle}).click();// Different ways to work with elements on page}}
I have a situation when someButtonInMenu need to be find inside parent element or vice versa without parent element
Activity
imartinflores commentedon Apr 21, 2022
Can you not use something like:
kenny-f commentedon Apr 21, 2022
That solves the problem if
childis always underparent.But what if in some instances
childis not underparent.Sorry, not good naming in the example.
imartinflores commentedon Apr 21, 2022
It's alright. In that case, could you not use a string instead of a Locator object ?
So you can create your selector, and then chain it or not depending if it's under the parent, i.e:
And then if it's under, you can use:
this.parent.locator(this.childSelector);The childSelector could also be a const instead, I assume you won't need to update it.
Does it make sense ? Sorry if I am confused.
kenny-f commentedon Apr 21, 2022
Yes, both your proposed solutions make sense and I have considered them.
Maybe I wasn't clear enough as to why I'm requesting this feature.
It's not that there is currently anything blocking me from achieving my use case.
It's more of a convenience thing.
It would be good if you could get away with only having locators in the page object instead of having a mix of locators and string selectors. Keeps the class smaller and cleaner IMO.
Feel free to close if you don't think this is something that is worth pursuing.
fbasso commentedon Apr 25, 2022
My two cents, and another approach on this topic
On our project, we are using this pattern (in its basic idea):
So that you can instantiate your page object either with the page instance, or a parent locator.
CtrlZvi commentedon Aug 27, 2022
Is your use case covered by Locator filtering? Specifically using the
hasfilter with the child locator?kenny-f commentedon Aug 30, 2022
CtrlZvi Yes! Locator filtering will work!
Thanks!
crazyk2 commentedon Mar 20, 2023
I want to click on
menuLocatorand then on$someButtonInMenu.It would be great if I work only with locators (define them in top of class), but I have to mix locators and selectors (strings) now
I have a situation when
someButtonInMenuneed to be find inside parent element or vice versa without parent elementdgozman commentedon Mar 23, 2023
Chaining locators is now possible with
Locator.locator()method. Check out this guide.kenny-f commentedon Mar 25, 2023
Dmitry Gozman (@dgozman) amazing, thanks!