Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/locator.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class Locator {
return
}

// Try to parse JSON strings that look like objects
if (this.parsedJsonAsString(locator)) {
return
}

this.type = defaultType || 'fuzzy'
this.output = locator
this.value = locator
Expand Down Expand Up @@ -89,6 +94,33 @@ class Locator {
return { [this.type]: this.value }
}

parsedJsonAsString(locator) {
if (typeof locator !== 'string') {
return false
}

const trimmed = locator.trim()
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
return false
}

try {
const parsed = JSON.parse(trimmed)
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
this.locator = parsed
this.type = Object.keys(parsed)[0]
this.value = parsed[this.type]
this.strict = true

Locator.filters.forEach(f => f(parsed, this))
return true
}
} catch (e) {
// continue with normal string processing
}
return false
}

/**
* @returns {string}
*/
Expand Down
91 changes: 91 additions & 0 deletions test/unit/locator_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,97 @@ describe('Locator', () => {
})
})

describe('JSON string parsing', () => {
it('should parse JSON string to css locator', () => {
const jsonStr = '{"css": "#button"}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('css')
expect(l.value).to.equal('#button')
})

it('should parse JSON string to xpath locator', () => {
const jsonStr = '{"xpath": "//div[@class=\\"test\\"]"}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('xpath')
expect(l.value).to.equal('//div[@class="test"]')
})

it('should parse JSON string to id locator', () => {
const jsonStr = '{"id": "my-element"}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('id')
expect(l.value).to.equal('my-element')
})

it('should parse JSON string to custom locator', () => {
const jsonStr = '{"byRole": "button"}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('byRole')
expect(l.value).to.equal('button')
})

it('should handle whitespace around JSON string', () => {
const jsonStr = ' { "css": ".test" } '
const l = new Locator(jsonStr)
expect(l.type).to.equal('css')
expect(l.value).to.equal('.test')
})

it('should reject invalid JSON and treat as string', () => {
const l = new Locator('{ invalid json')
expect(l.type).to.equal('fuzzy')
expect(l.value).to.equal('{ invalid json')
})

it('should handle aria-style locators with multiple properties', () => {
// This tests that only the first property is used (as per simple key-value requirement)
const jsonStr = '{"role": "button", "text": "Save"}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('role')
expect(l.value).to.equal('button')
expect(l.strict).to.equal(true)
})

it('should ignore non-object JSON', () => {
const jsonStr = '"just a string"'
const l = new Locator(jsonStr)
expect(l.type).to.equal('fuzzy')
expect(l.value).to.equal('"just a string"')
})

it('should work with array values for certain locators', () => {
const jsonStr = '{"shadow": ["app", "component", "button"]}'
const l = new Locator(jsonStr)
expect(l.type).to.equal('shadow')
expect(l.value).to.eql(['app', 'component', 'button'])
})

it('should mark parsed locators as strict', () => {
const jsonStr = '{"css": "#test"}'
const l = new Locator(jsonStr)
expect(l.strict).to.equal(true)
})

it('should demonstrate equivalence between object and JSON string locators', () => {
// Same locator, different formats
const objectLocator = new Locator({ css: '#main-button' })
const jsonLocator = new Locator('{"css": "#main-button"}')

expect(objectLocator.type).to.equal(jsonLocator.type)
expect(objectLocator.value).to.equal(jsonLocator.value)
expect(objectLocator.strict).to.equal(jsonLocator.strict)
})

it('should work with complex xpath in JSON', () => {
const jsonStr = '{"xpath": "//div[contains(@class, \\"container\\")]//button"}'
const l = new Locator(jsonStr)

expect(l.type).to.equal('xpath')
expect(l.value).to.equal('//div[contains(@class, "container")]//button')
expect(l.simplify()).to.equal('//div[contains(@class, "container")]//button')
})
})

it('should transform CSS to xpath', () => {
const l = new Locator('p > #user', 'css')
const nodes = xpath.select(l.toXPath(), doc)
Expand Down
Loading