8000 Add getByRole in the browser module by ankur22 · Pull Request #4843 · grafana/k6 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add getByRole in the browser module #4843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: master
Choose a base branch
from

Conversation

ankur22
Copy link
Contributor
@ankur22 ankur22 commented Jun 9, 2025

What?

This PR adds a long awaited feature which is page.getByRole. It makes working with selectors slightly simpler. More getBy* APIs are in the works.

Why?

It is a lot simpler to work with getByRole instead of XPath or CSS selectors in most cases.

Checklist

  • I have performed a self-review of my code.
  • I have 8000 commented on my code, particularly in hard-to-understand areas.
  • I have added tests for my changes.
  • I have run linter and tests locally (make check) and all pass.

Checklist: Documentation (only for k6 maintainers and if relevant)

Please do not merge this PR until the following items are filled out.

  • I have added the correct milestone and labels to the PR.
  • I have updated the release notes: link
  • I have updated or added an issue to the k6-documentation: grafana/k6-docs#NUMBER if applicable
  • I have updated or added an issue to the TypeScript definitions: grafana/k6-DefinitelyTyped#NUMBER if applicable

Related PR(s)/Issue(s)

#4764
#4320
#4248

@ankur22 ankur22 requested a review from a team as a code owner June 9, 2025 21:23
@ankur22 ankur22 requested review from inancgumus and joanlopez and removed request for a team June 9, 2025 21:23
@@ -14,6 +14,1259 @@
* limitations under the License.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main implementation is in the injected_script.js file. The code was directly copied over from the compiled JS code from Playwright. I have added as many integration tests as possible to ensure that all the possible flows are accounted for. While i understand the jist of what is happening in the copied code, some of it is still a bit of a mystery as to why things are done this way. A lot of the code seems to be inherited behaviour from specifications, such as not being able to work with header or footer unless they're not encapsulated -- exact spec is unknown though.

Do we need to add the Playwright license and notice file in the browser module?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to add another license note, as we already have this one:

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@ankur22 ankur22 force-pushed the poc/role-based-selector-engine branch from f35ebc0 to a07de3f Compare June 9, 2025 21:31
@@ -225,6 +1478,7 @@ class InjectedScript {
css: new CSSQueryEngine(),
text: new TextQueryEngine(),
xpath: new XPathQueryEngine(),
'internal:role': createRoleEngine(true),
Copy link
Contributor Author
@ankur22 ankur22 Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sticking with how Playwright has labelled it. There is also a selector without internal which we could consider adding. It is so that users can query without the getByRole API, and directly do something like: page.locator('role=button');.

@ankur22 ankur22 force-pushed the poc/role-based-selector-engine branch 2 times, most recently from e684a3b to 7919b29 Compare June 9, 2025 21:42
@@ -0,0 +1,775 @@
package tests
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are failing on Windows for an unknown reason. I can run the same test in a script file and it works on Windows. I'm looking into it 😕

Copy link
Contributor Author
@ankur22 ankur22 Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WINDOWS! WHY ART THOU SO ANNOYING! I WANT TO LOVE YOU BUT I CAN'T 😭

I tried to run some other tests which work with page.locator and they don't work! These tests are in keyboard_test.go, and at the top of the file there is this comment:

// practically none of this work on windows
//go:build !windows

I will do the same for the new tests 😢

@ankur22 ankur22 force-pushed the poc/role-based-selector-engine branch from a766b35 to e172850 Compare June 10, 2025 14:19
@ankur22 ankur22 added this to the v1.1.0 milestone Jun 10, 2025
@@ -1002,6 +1002,58 @@ func (f *Frame) getAttribute(selector, name string, opts *FrameBaseOptions) (str
return s, true, nil
}

// Locator creates and returns a new locator for this frame.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This GoDoc sentence looks like a leftover from copy-pasting 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in f490d70

@@ -1002,6 +1002,58 @@ func (f *Frame) getAttribute(selector, name string, opts *FrameBaseOptions) (str
return s, true, nil
}

// Locator creates and returns a new locator for this frame.
func (f *Frame) GetByRole(role string, opts *GetByRoleOptions) *Locator {
f.log.Debugf("Frame:Locator", "fid:%s furl:%q role:%q opts:%+v", f.ID(), f.URL(), role, opts)
Copy link
Contributor
@joanlopez joanlopez Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we refer to the method name instead, to keep it consistent with the other methods? 🤔

Suggested change
f.log.Debugf("Frame:Locator", "fid:%s furl:%q role:%q opts:%+v", f.ID(), f.URL(), role, opts)
f.log.Debugf("Frame:GetByRole", "fid:%s furl:%q role:%q opts:%+v", f.ID(), f.URL(), role, opts)

8000 Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in f490d70

Comment on lines 1015 to 1028
if opts.Checked != nil {
properties["checked"] = fmt.Sprintf("%v", *opts.Checked)
}
if opts.Disabled != nil {
properties["disabled"] = fmt.Sprintf("%v", *opts.Disabled)
}
if opts.Selected != nil {
properties["selected"] = fmt.Sprintf("%v", *opts.Selected)
}
if opts.Expanded != nil {
properties["expanded"] = fmt.Sprintf("%v", *opts.Expanded)
}
if opts.IncludeHidden != nil {
properties["include-hidden"] = fmt.Sprintf("%v", *opts.IncludeHidden)
Copy link
Contributor
@joanlopez joanlopez Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not using strconv.FormatBool instead of string formatting here? It's slightly more idiomatic, and perhaps even a bit faster, I guess. But maybe there's a reason not to.

PS: Same, whenever it applies, with the rest of the opts parsed on this method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good spot. Changed in a098ac3

Comment on lines 15 to 25
func stringPtr(s string) *string {
return &s
}

func boolPtr(b bool) *bool {
return &b
}

func int64Ptr(i int64) *int64 {
return &i
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think nowadays, thanks to generics, this can be simplified into a single generic method, like:

func ptr[T any](v T) *T {
  return &v
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Great idea. Updated in dfcd80f

ankur22 added 14 commits June 16, 2025 16:23
I basically copied the JS compiled injected script from PW, and pasted
it into k6's injected script. It seems to work with basic role based
selectors.
These tests include options and working with different attributes
under-the-hood to do the same thing with a single option or role value.
It seems to be an issue with locator based APIs in integration tests,
they just don't work. Locator based tests work perfectly when written
and ran from a js test script.
The role based selector engine needs to work with ' and not " when it
comes to the name option. This is automatically done for the user in
the mapping layer, but in the integration test we need to ensure that
we explicitly use ' and not ".
@ankur22 ankur22 force-pushed the poc/role-based-selector-engine branch from 74171be to be4b038 Compare June 16, 2025 15:23
@ankur22 ankur22 requested a review from joanlopez June 16, 2025 15:52
Copy link
Contributor
@inancgumus inancgumus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on the easy-to-review parts of this PR :-] I'll continue reviewing the rest tomorrow.

@@ -14,6 +14,1259 @@
* limitations under the License.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to add another license note, as we already have this one:

/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0