Skip to content

Commit 73d6258

Browse files
author
DavertMik
committed
feat(strict-mode): mark nested matches with explicit (inside N.) label
1 parent 327dcda commit 73d6258

2 files changed

Lines changed: 36 additions & 14 deletions

File tree

lib/helper/errors/MultipleElementsFound.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,44 @@ export function isAncestorXPath(ancestor, descendant) {
1414
return ancestorSegments.every((segment, index) => segment === descendantSegments[index])
1515
}
1616

17-
export function computeDepths(entries) {
18-
const depths = new Array(entries.length).fill(0)
17+
export function computeParents(entries) {
18+
const parents = new Array(entries.length).fill(-1)
1919
const stack = []
2020
for (let i = 0; i < entries.length; i++) {
2121
const xpath = entries[i].xpath
2222
if (!xpath) continue
2323
while (stack.length > 0 && !isAncestorXPath(entries[stack[stack.length - 1]].xpath, xpath)) {
2424
stack.pop()
2525
}
26-
depths[i] = stack.length
26+
parents[i] = stack.length > 0 ? stack[stack.length - 1] : -1
2727
stack.push(i)
2828
}
29-
return depths
29+
return parents
30+
}
31+
32+
export function computeDepths(entries) {
33+
const parents = computeParents(entries)
34+
return parents.map((parent, i) => {
35+
if (!entries[i].xpath) return 0
36+
let depth = 0
37+
let current = parent
38+
while (current !== -1) {
39+
depth++
40+
current = parents[current]
41+
}
42+
return depth
43+
})
3044
}
3145

32-
export function formatTree(entries, depths) {
46+
export function formatTree(entries, depths, parents) {
3347
return entries.map((entry, i) => {
3448
const pad = ' '.repeat(depths[i] || 0)
3549
if (entry.error) {
3650
return `${pad} ${entry.index}. [Unable to get element info: ${entry.error}]`
3751
}
38-
return `${pad} ${entry.index}. > ${entry.xpath}\n${pad} ${entry.html}`
52+
const parentPos = parents ? parents[i] : -1
53+
const nesting = parentPos !== undefined && parentPos !== -1 ? ` (inside ${entries[parentPos].index}.)` : ''
54+
return `${pad} ${entry.index}.${nesting} > ${entry.xpath}\n${pad} ${entry.html}`
3955
})
4056
}
4157

@@ -70,7 +86,7 @@ class MultipleElementsFound extends Error {
7086
}
7187
}
7288

73-
const items = formatTree(entries, computeDepths(entries))
89+
const items = formatTree(entries, computeDepths(entries), computeParents(entries))
7490

7591
if (this.count > 10) {
7692
items.push(` ... and ${this.count - 10} more`)

test/unit/multiple_elements_found_test.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect } from 'chai'
22
import MultipleElementsFound, {
33
computeDepths,
4+
computeParents,
45
formatTree,
56
isAncestorXPath,
67
splitXPath,
@@ -48,21 +49,26 @@ describe('MultipleElementsFound tree formatting', () => {
4849
{ index: 2, xpath: '//html/body/div[1]/div[1]', html: '<div class="item">' },
4950
{ index: 3, xpath: '//html/body/div[1]/div[2]', html: '<div class="item">' },
5051
]
52+
expect(computeParents(entries)).to.deep.equal([-1, 0, 0])
5153
expect(computeDepths(entries)).to.deep.equal([0, 1, 1])
52-
const items = formatTree(entries, [0, 1, 1])
54+
const items = formatTree(entries, [0, 1, 1], [-1, 0, 0])
5355
expect(items[0]).to.equal(' 1. > //html/body/div[1]\n <div class="item">')
54-
expect(items[1]).to.equal(' 2. > //html/body/div[1]/div[1]\n <div class="item">')
55-
expect(items[2]).to.equal(' 3. > //html/body/div[1]/div[2]\n <div class="item">')
56+
expect(items[1]).to.equal(' 2. (inside 1.) > //html/body/div[1]/div[1]\n <div class="item">')
57+
expect(items[2]).to.equal(' 3. (inside 1.) > //html/body/div[1]/div[2]\n <div class="item">')
5658
})
5759

58-
it('supports deeper nesting and returns to root level', () => {
60+
it('marks the immediate parent for deeper nesting', () => {
5961
const entries = [
6062
{ index: 1, xpath: '//html/body/div[1]', html: '<div>' },
6163
{ index: 2, xpath: '//html/body/div[1]/ul', html: '<ul>' },
6264
{ index: 3, xpath: '//html/body/div[1]/ul/li', html: '<li>' },
6365
{ index: 4, xpath: '//html/body/div[2]', html: '<div>' },
6466
]
67+
expect(computeParents(entries)).to.deep.equal([-1, 0, 1, -1])
6568
expect(computeDepths(entries)).to.deep.equal([0, 1, 2, 0])
69+
const items = formatTree(entries, [0, 1, 2, 0], [-1, 0, 1, -1])
70+
expect(items[2]).to.include('3. (inside 2.) >')
71+
expect(items[3]).to.equal(' 4. > //html/body/div[2]\n <div>')
6672
})
6773

6874
it('renders failed lookups as roots and keeps global numbering', async () => {
@@ -74,7 +80,7 @@ describe('MultipleElementsFound tree formatting', () => {
7480
await err.fetchDetails()
7581
expect(err.message).to.include(' 1. > //html/body/div[1]')
7682
expect(err.message).to.include(' 2. [Unable to get element info: detached]')
77-
expect(err.message).to.include(' 3. > //html/body/div[1]/div[1]')
83+
expect(err.message).to.include(' 3. (inside 1.) > //html/body/div[1]/div[1]')
7884
})
7985

8086
it('renders nested fetchDetails output with indentation', async () => {
@@ -86,8 +92,8 @@ describe('MultipleElementsFound tree formatting', () => {
8692
await err.fetchDetails()
8793
const lines = err.message.split('\n')
8894
expect(lines[1]).to.equal(' 1. > //html/body/div[1]')
89-
expect(lines[3]).to.equal(' 2. > //html/body/div[1]/div[1]')
90-
expect(lines[5]).to.equal(' 3. > //html/body/div[1]/div[2]')
95+
expect(lines[3]).to.equal(' 2. (inside 1.) > //html/body/div[1]/div[1]')
96+
expect(lines[5]).to.equal(' 3. (inside 1.) > //html/body/div[1]/div[2]')
9197
expect(err.message).to.include('Use a more specific locator')
9298
})
9399
})

0 commit comments

Comments
 (0)