internal(browser): Add exact option to text-based locators#1152
internal(browser): Add exact option to text-based locators#1152going-confetti merged 8 commits intomainfrom
Conversation
e957118 to
5b99b3f
Compare
5b99b3f to
58cd8ea
Compare
| } | ||
|
|
||
| export interface TextLocatorOptionsExpression { | ||
| type: 'TextLocatorOptionsExpression' |
There was a problem hiding this comment.
Maybe something like TextBasedLocatorOptionsExpression is a better choice, since getByText exists?
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Invalid CSS syntax for negating a variable
- Replaced the invalid
-var(--space-1)expression withcalc(var(--space-1) * -1)so the negative margin-right is applied correctly.
- Replaced the invalid
Or push these changes by commenting:
@cursor push 9c69db7e29
Preview (9c69db7e29)
diff --git a/src/views/BrowserTestEditor/ActionForms/components/TextFieldWithExactToggle.tsx b/src/views/BrowserTestEditor/ActionForms/components/TextFieldWithExactToggle.tsx
--- a/src/views/BrowserTestEditor/ActionForms/components/TextFieldWithExactToggle.tsx
+++ b/src/views/BrowserTestEditor/ActionForms/components/TextFieldWithExactToggle.tsx
@@ -41,7 +41,7 @@
onBlur?.()
}}
css={css`
- margin-right: -var(--space-1);
+ margin-right: calc(var(--space-1) * -1);
`}
>
<WholeWordIcon />You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Element highlighting ignores the new exact matching option
findElementsBySelectornow passesexact: selector.text.exact ?? falseto text-based testing-library queries so highlight matching aligns with generated Playwright locator behavior.
Or push these changes by commenting:
@cursor push 3d3e95dd43
Preview (3d3e95dd43)
diff --git a/src/utils/selectors.ts b/src/utils/selectors.ts
--- a/src/utils/selectors.ts
+++ b/src/utils/selectors.ts
@@ -31,19 +31,29 @@
})
case 'alt':
- return queryAllByAltText(container, selector.text.value)
+ return queryAllByAltText(container, selector.text.value, {
+ exact: selector.text.exact ?? false,
+ })
case 'label':
- return queryAllByLabelText(container, selector.text.value)
+ return queryAllByLabelText(container, selector.text.value, {
+ exact: selector.text.exact ?? false,
+ })
case 'placeholder':
- return queryAllByPlaceholderText(container, selector.text.value)
+ return queryAllByPlaceholderText(container, selector.text.value, {
+ exact: selector.text.exact ?? false,
+ })
case 'text':
- return queryAllByText(container, selector.text.value)
+ return queryAllByText(container, selector.text.value, {
+ exact: selector.text.exact ?? false,
+ })
case 'title':
- return queryAllByTitle(container, selector.text.value)
+ return queryAllByTitle(container, selector.text.value, {
+ exact: selector.text.exact ?? false,
+ })
default:
return []You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Existing saved locators silently lose exact matching behavior
toNodeSelectornow defaults text-based locatorexacttotruewhen legacy saved locator options are missing, preserving prior exact-matching behavior while still honoring explicitfalse.
Or push these changes by commenting:
@cursor push 775b53775b
Preview (775b53775b)
diff --git a/src/codegen/browser/selectors.ts b/src/codegen/browser/selectors.ts
--- a/src/codegen/browser/selectors.ts
+++ b/src/codegen/browser/selectors.ts
@@ -141,25 +141,28 @@
case 'alt':
return {
type: 'alt',
- text: { value: locator.text, exact: locator.options?.exact },
+ text: { value: locator.text, exact: locator.options?.exact ?? true },
}
case 'label':
return {
type: 'label',
- text: { value: locator.label, exact: locator.options?.exact },
+ text: { value: locator.label, exact: locator.options?.exact ?? true },
}
case 'placeholder':
return {
type: 'placeholder',
- text: { value: locator.placeholder, exact: locator.options?.exact },
+ text: {
+ value: locator.placeholder,
+ exact: locator.options?.exact ?? true,
+ },
}
case 'title':
return {
type: 'title',
- text: { value: locator.title, exact: locator.options?.exact },
+ text: { value: locator.title, exact: locator.options?.exact ?? true },
}
case 'text':
diff --git a/src/codegen/browser/test.test.ts b/src/codegen/browser/test.test.ts
--- a/src/codegen/browser/test.test.ts
+++ b/src/codegen/browser/test.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest'
+import { toNodeSelector } from './selectors'
import { convertEventsToTest } from './test'
describe('convertEventsToTest', () => {
@@ -81,3 +82,20 @@
})
})
})
+
+describe('toNodeSelector', () => {
+ it('should default text locator exact matching to true when options are missing', () => {
+ const selector = toNodeSelector({
+ type: 'label',
+ label: 'Username',
+ })
+
+ expect(selector).toEqual({
+ type: 'label',
+ text: {
+ value: 'Username',
+ exact: true,
+ },
+ })
+ })
+})You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 053e705. Configure here.
| type: 'TextLocatorOptionsExpression', | ||
| exact: node.selector.text.exact, | ||
| } | ||
| : null, |
There was a problem hiding this comment.
Existing saved locators silently lose exact matching behavior
Medium Severity
Previously, the code generation hardcoded { exact: true } for all text-based locators. Now it only emits { exact: true } when node.selector.text.exact is truthy. For existing saved ActionLocator data that predates this PR (which lacks the options field), toNodeSelector maps locator.options?.exact to undefined, causing the intermediate layer to set options: null. This silently changes the generated script from page.getByLabel("text", { exact: true }) to page.getByLabel("text"), switching from exact to substring matching in Playwright/k6 — a behavioral regression that could cause tests to match unintended elements.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 053e705. Configure here.
There was a problem hiding this comment.
This is fine, because it's not a user-facing feature yet. The user-facing recording-to-script conversion should not be affected.
| onChange={(e) => onValueChange(e.target.value)} | ||
| onBlur={onBlur} | ||
| > | ||
| {value.trim().length > 0 && ( |
There was a problem hiding this comment.
I found it a bit weird that exact toggle dissapears when I clear input, what's the reasoning behind this?
There was a problem hiding this comment.
Good question. I thought it didn't make sense to have this option while the input is empty, but I can see how it may look confusing when the button appears out of nowhere. I'll push a fix 👀
e-fisher
left a comment
There was a problem hiding this comment.
LGTM 🚀 Left a non-blocking question



Description
getByAltText,getByTitle,getByText, andgetByLabelexactoption is set, display it in the inline previewHow to Test
Checklist
Related PR(s)/Issue(s)
Note
Medium Risk
Updates locator data shapes and codegen to pass through optional
exactmatching for multiple locator types; risk is mainly around compatibility with existing serialized locator payloads and generated scripts changing behavior whenexactis set.Overview
Adds optional
exactmatching support across text-based locators (alt/label/placeholder/title/text) end-to-end: runner schemas and page proxies now accept/preserveoptions.exact, the selector model is updated to carry{ value, exact }, and DOM querying honors the flag when resolving selectors.Code generation is updated to emit locator option objects (via new IR
TextLocatorOptionsExpression) instead of forcing exact matching, and the test snapshots are updated accordingly. The Browser Test Editor UI gains a reusableTextFieldWithExactToggleand shows an inline exact match indicator in the locator preview.Reviewed by Cursor Bugbot for commit 5e84ddd. Bugbot is set up for automated code reviews on this repo. Configure here.