Skip to content

Commit 9404fda

Browse files
authored
feat(browser): Generate getByRole selectors (#877)
1 parent 871b16a commit 9404fda

File tree

34 files changed

+796
-186
lines changed

34 files changed

+796
-186
lines changed

extension/src/frontend/recording.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { nanoid } from 'nanoid'
33
import { BrowserEvent } from '@/schemas/recording'
44

55
import { BrowserExtensionClient } from '../messaging'
6-
import { generateSelectors } from '../selectors'
6+
import { getEventTarget } from '../target'
77
import {
88
findAssociatedElement,
99
findInteractiveElement,
@@ -113,7 +113,7 @@ export function startRecording(client: BrowserExtensionClient) {
113113
type: 'click',
114114
eventId: nanoid(),
115115
timestamp: Date.now(),
116-
target: { selectors: generateSelectors(clickTarget) },
116+
target: getEventTarget(clickTarget),
117117
button,
118118
modifiers: {
119119
ctrl: ev.ctrlKey,
@@ -130,7 +130,7 @@ export function startRecording(client: BrowserExtensionClient) {
130130
type: 'select-change',
131131
eventId: nanoid(),
132132
timestamp: Date.now(),
133-
target: { selectors: generateSelectors(target) },
133+
target: getEventTarget(target),
134134
selected: [...target.selectedOptions].map((option) => option.value),
135135
multiple: target.multiple,
136136
tab: getTabId(),
@@ -142,7 +142,7 @@ export function startRecording(client: BrowserExtensionClient) {
142142
type: 'input-change',
143143
eventId: nanoid(),
144144
timestamp: Date.now(),
145-
target: { selectors: generateSelectors(target) },
145+
target: getEventTarget(target),
146146
value: target.value,
147147
sensitive: false,
148148
tab: getTabId(),
@@ -165,7 +165,7 @@ export function startRecording(client: BrowserExtensionClient) {
165165
type: 'check-change',
166166
eventId: nanoid(),
167167
timestamp: Date.now(),
168-
target: { selectors: generateSelectors(target) },
168+
target: getEventTarget(target),
169169
checked: target.checked,
170170
tab: getTabId(),
171171
})
@@ -182,7 +182,7 @@ export function startRecording(client: BrowserExtensionClient) {
182182
type: 'radio-change',
183183
eventId: nanoid(),
184184
timestamp: Date.now(),
185-
target: { selectors: generateSelectors(target) },
185+
target: getEventTarget(target),
186186
name: target.name,
187187
value: target.value,
188188
tab: getTabId(),
@@ -195,7 +195,7 @@ export function startRecording(client: BrowserExtensionClient) {
195195
type: 'input-change',
196196
eventId: nanoid(),
197197
timestamp: Date.now(),
198-
target: { selectors: generateSelectors(target) },
198+
target: getEventTarget(target),
199199
value: target.value,
200200
sensitive: target.type === 'password',
201201
tab: getTabId(),
@@ -249,8 +249,8 @@ export function startRecording(client: BrowserExtensionClient) {
249249
type: 'submit-form',
250250
eventId: nanoid(),
251251
timestamp: Date.now(),
252-
form: { selectors: generateSelectors(ev.target) },
253-
submitter: { selectors: generateSelectors(ev.submitter) },
252+
form: getEventTarget(ev.target),
253+
submitter: getEventTarget(ev.submitter),
254254
tab: getTabId(),
255255
})
256256
})

extension/src/frontend/view/ElementInspector/ElementInspector.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,7 @@ export function ElementInspector({ onClose }: ElementInspectorProps) {
9494
eventId: nanoid(),
9595
timestamp: Date.now(),
9696
tab: getTabId(),
97-
target: {
98-
selectors: {
99-
css: assertion.selector,
100-
},
101-
},
97+
target: assertion.target,
10298
assertion: toAssertion(assertion),
10399
},
104100
],
@@ -132,9 +128,9 @@ export function ElementInspector({ onClose }: ElementInspectorProps) {
132128
`}
133129
>
134130
<div>Tag</div>
135-
<div>{element.target.tagName.toLowerCase()}</div>
131+
<div>{element.element.tagName.toLowerCase()}</div>
136132
<div>Selector</div>
137-
<div>{element.selector.css}</div>
133+
<div>{element.target.selectors.css}</div>
138134
<div>Roles</div>
139135
<div>{formatRoles(element.roles)}</div>
140136
</div>

extension/src/frontend/view/ElementInspector/ElementMenu.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ function CheckboxAssertion({
9090
function handleAddCheckAssertion() {
9191
onAddAssertion({
9292
type: 'check',
93-
selector: input.selector.css,
93+
target: input.target,
9494
inputType: isNative(role, input.element) ? 'native' : 'aria',
9595
expected: getCheckedState(input.element),
9696
})
@@ -116,7 +116,7 @@ function TextInputAssertion({
116116
const handleAddAssertion = () => {
117117
onAddAssertion({
118118
type: 'text-input',
119-
selector: input.selector.css,
119+
target: input.target,
120120
multiline:
121121
input.element instanceof HTMLTextAreaElement ||
122122
input.element.getAttribute('aria-multiline') === 'true',
@@ -193,16 +193,16 @@ export function ElementMenu({ element, onSelectAssertion }: ElementMenuProps) {
193193
const handleAddVisibilityAssertion = () => {
194194
onSelectAssertion({
195195
type: 'visibility',
196-
selector: element.selector.css,
196+
target: element.target,
197197
state: 'visible',
198198
})
199199
}
200200

201201
const handleAddTextAssertion = () => {
202202
onSelectAssertion({
203203
type: 'text',
204-
selector: element.selector.css,
205-
text: element.target.textContent ?? '',
204+
target: element.target,
205+
text: element.element.textContent ?? '',
206206
})
207207
}
208208

extension/src/frontend/view/ElementInspector/ElementMenu.utils.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { ElementSelector } from '@/schemas/recording'
2-
import { generateSelectors } from 'extension/src/selectors'
1+
import { BrowserEventTarget } from '@/schemas/recording'
2+
import { getEventTarget } from 'extension/src/target'
33
import { ElementRole, getElementRoles } from 'extension/src/utils/aria'
44
import { findAssociatedElement } from 'extension/src/utils/dom'
55

@@ -18,30 +18,30 @@ function* getAncestors(element: Element) {
1818

1919
export interface LabeledControl {
2020
element: Element
21-
selector: ElementSelector
21+
target: BrowserEventTarget
2222
roles: ElementRole[]
2323
}
2424

2525
export function findAssociatedControl({
26+
element,
2627
target,
27-
selector,
2828
roles,
2929
}: TrackedElement): LabeledControl | null {
3030
// If the target is already a control, then we don't need to do a search.
3131
if (
32-
target instanceof HTMLInputElement ||
33-
target instanceof HTMLButtonElement ||
34-
target instanceof HTMLSelectElement ||
35-
target instanceof HTMLTextAreaElement
32+
element instanceof HTMLInputElement ||
33+
element instanceof HTMLButtonElement ||
34+
element instanceof HTMLSelectElement ||
35+
element instanceof HTMLTextAreaElement
3636
) {
3737
return {
38-
element: target,
39-
selector,
38+
element,
39+
target,
4040
roles,
4141
}
4242
}
4343

44-
const label = [...getAncestors(target)].find(
44+
const label = [...getAncestors(element)].find(
4545
(ancestor) => ancestor instanceof HTMLLabelElement
4646
)
4747

@@ -57,7 +57,7 @@ export function findAssociatedControl({
5757

5858
return {
5959
element: associatedElement,
60-
selector: generateSelectors(associatedElement),
60+
target: getEventTarget(associatedElement),
6161
roles: [...getElementRoles(associatedElement)],
6262
}
6363
}

extension/src/frontend/view/ElementInspector/ElementPopover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ ElementPopover.Selector = function ElementSelector({
9797
flex: 1 1 0;
9898
`}
9999
>
100-
{element.selector.css}
100+
{element.target.selectors.css}
101101
</ElementPopover.Heading>
102102
<Tooltip asChild content="Select child element">
103103
<IconButton disabled={onContract === undefined} onClick={onContract}>

extension/src/frontend/view/ElementInspector/assertions/types.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1-
import { CheckState } from '@/schemas/recording'
1+
import { BrowserEventTarget, CheckState } from '@/schemas/recording'
22

33
export interface CheckAssertionData {
44
type: 'check'
5-
selector: string
5+
target: BrowserEventTarget
66
inputType: 'aria' | 'native'
77
expected: CheckState
88
}
99

1010
export interface VisibilityAssertionData {
1111
type: 'visibility'
12-
selector: string
12+
target: BrowserEventTarget
1313
state: 'visible' | 'hidden'
1414
}
1515

1616
export interface TextInputAssertionData {
1717
type: 'text-input'
18-
selector: string
18+
target: BrowserEventTarget
1919
multiline: boolean
2020
expected: string
2121
}
2222

2323
export interface TextAssertionData {
2424
type: 'text'
25-
selector: string
25+
target: BrowserEventTarget
2626
text: string
2727
}
2828

extension/src/frontend/view/ElementInspector/hooks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function usePinnedElement(element?: TrackedElement | undefined) {
2525
return undefined
2626
}
2727

28-
const parent = head.target.parentElement
28+
const parent = head.element.parentElement
2929

3030
if (parent === null || parent === document.documentElement) {
3131
return undefined
@@ -71,7 +71,7 @@ export function useElementHighlight(element: TrackedElement | null) {
7171
type: 'highlight-elements',
7272
selector: element && {
7373
type: 'css',
74-
selector: element.selector.css,
74+
selector: element.target.selectors.css,
7575
},
7676
})
7777
}, [client, element])
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { nanoid } from 'nanoid'
22

3-
import { ElementSelector } from '@/schemas/recording'
3+
import { BrowserEventTarget } from '@/schemas/recording'
4+
import { getEventTarget } from 'extension/src/target'
45
import { ElementRole, getElementRoles } from 'extension/src/utils/aria'
56

6-
import { generateSelectors } from '../../../selectors'
77
import { Bounds } from '../types'
88
import { getElementBounds } from '../utils'
99

1010
export interface TrackedElement {
1111
id: string
1212
roles: ElementRole[]
13-
selector: ElementSelector
14-
target: Element
13+
target: BrowserEventTarget
14+
element: Element
1515
bounds: Bounds
1616
}
1717

@@ -21,8 +21,8 @@ export function toTrackedElement(element: Element): TrackedElement {
2121
return {
2222
id: nanoid(),
2323
roles: [...roles],
24-
selector: generateSelectors(element),
25-
target: element,
24+
target: getEventTarget(element),
25+
element: element,
2626
bounds: getElementBounds(element),
2727
}
2828
}

extension/src/frontend/view/TextSelectionPopover.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function TextSelectionPopoverContent({
3131

3232
const [assertion, setAssertion] = useState<TextAssertionData>({
3333
type: 'text',
34-
selector: selection.element.selector.css,
34+
target: selection.element.target,
3535
text: selection.text,
3636
})
3737

@@ -46,7 +46,7 @@ function TextSelectionPopoverContent({
4646
const handleSubmit = (assertion: TextAssertionData) => {
4747
onAdd({
4848
...assertion,
49-
selector: targetElement.selector.css,
49+
target: targetElement.target,
5050
})
5151

5252
onClose()
@@ -98,11 +98,7 @@ export function TextSelectionPopover({ onClose }: TextSelectionPopoverProps) {
9898
timestamp: Date.now(),
9999
type: 'assert',
100100
tab: getTabId(),
101-
target: {
102-
selectors: {
103-
css: assertion.selector,
104-
},
105-
},
101+
target: assertion.target,
106102
assertion: {
107103
type: 'text',
108104
operation: {

extension/src/selectors.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)