Usually, we can just chain two locators with `>>` to implement
`Locator.locator(locator)`. However, this does not play nicely with more
advanced inner locators like `or` and `and`:
```ts
const child = page.locator('input').or(page.locator('button'));
page.locator('parent').locator(child);
```
One would expect the above to locate "input or button" inside a
"parent". However, currently it locates "input inside a parent" or
"button", because it's translated to `parent >> input >>
internal:or="button"`.
To fix this, we have to wrap inner locator into `internal:chain` and
query it separately from the parent.
Fixes#23724.
So, the following will work:
```
page.frameLocator('iframe').locator('span').or(page.frameLoactor('iframe').locator('div'))
```
The following will not work, because frame locators are not exactly the
same:
```
page.frameLocator('#iframe1').locator('span').or(page.frameLoactor('#iframe2').locator('div'))
```
Also improve the error message to be more readable and include the
locator.
Fixes#23697.
- Rename internal selectors `has`, `control` and `attr` to
`internal:has`, `internal:control` and `internal:attr`.
- Fix `getByLabel()` to respect strictness, by introducing
`internal:label` selector.
- Move tests essential for ports to `selectors-by.spec`.
This introduces `role=button[name="Click me"][pressed]` attribute-style
role selector. It is only available under `env.PLAYWRIGHT_EXPERIMENTAL_FEATURES`.
Supported attributes:
- `role` is required, for example `role=button`;
- `name` is accessible name, supports matching operators and regular expressions:
`role=button[name=/Click(me)?/]`;
- `checked` boolean/mixed, for example `role=checkbox[checked=false]`;
- `selected` boolean, for example `role=option[selected]`;
- `expanded` boolean, for example `role=button[expanded=true]`;
- `disabled` boolean, for example `role=button[disabled]`;
- `level` number, for example `role=heading[level=3]`;
- `pressed` boolean/mixed, for example `role=button[pressed="mixed"]`;
- `includeHidden` - by default, only non-hidden elements are considered.
Passing `role=button[includeHidden]` matches hidden elements as well.
This introduces `locator('div', { has: locator })` syntax that matches elements containing other elements.
Can be used together with `hasText`.
Internally, has selector engine takes an inner selector escaped with double-quotes:
`div >> has="li >> span >> text=Foo" >> span`.