Skip to content
This repository was archived by the owner on Oct 13, 2022. It is now read-only.

Commit 6282e1b

Browse files
authored
feat: retry assertion that follows xpath command (#12)
* chore: refactor get value * feat: add retries, close #3
1 parent a2f2a83 commit 6282e1b

File tree

4 files changed

+80
-58
lines changed

4 files changed

+80
-58
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ it('finds list items', () => {
2525
})
2626
```
2727

28+
**note:** you can test XPath expressions from DevTools console using `$x(...)` function, for example `$x('//div')` to find all divs.
29+
2830
See [cypress/integration/spec.js](cypress/integration/spec.js)
2931

3032
## Roadmap
3133

3234
- [x] wrap returned DOM nodes in jQuery [#2](https://github.com/cypress-io/cypress-xpath/issues/2)
33-
- [ ] retry the assertion that follows [#3](https://github.com/cypress-io/cypress-xpath/issues/3)
35+
- [x] retry the assertion that follows [#3](https://github.com/cypress-io/cypress-xpath/issues/3)
3436
- [ ] add TypeScript definitions [#4](https://github.com/cypress-io/cypress-xpath/issues/4)
3537
- [ ] search from the previous subject element [#5](https://github.com/cypress-io/cypress-xpath/issues/5)
3638

cypress/integration/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22
<main>
33
<h1>cypress-xpath</h1>
44
</main>
5+
<script>
6+
setTimeout(() => {
7+
document.querySelector('main').insertAdjacentHTML('afterend',
8+
'<div id="inserted">inserted text</div>')
9+
}, 1000)
10+
</script>
511
</body>

cypress/integration/spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ describe('cypress-xpath', () => {
2424
cy.xpath('//h1/text()').its('0.textContent').should('equal', 'cypress-xpath')
2525
})
2626

27+
it('retries until element is inserted', () => {
28+
// the element will be inserted after 1 second
29+
cy.xpath('string(//*[@id="inserted"])').should('equal', 'inserted text')
30+
})
31+
2732
describe('primitives', () => {
2833
it('counts h1 elements', () => {
2934
cy.xpath('count(//h1)').should('equal', 1)

src/index.js

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
})
1414
```
1515
*/
16-
const xpath = (selector) => {
16+
const xpath = (selector, options = {}) => {
1717
/* global XPathResult */
1818
const isNumber = (xpathResult) => xpathResult.resultType === XPathResult.NUMBER_TYPE
1919
const numberResult = (xpathResult) => xpathResult.numberValue
@@ -24,87 +24,96 @@ const xpath = (selector) => {
2424
const isBoolean = (xpathResult) => xpathResult.resultType === XPathResult.BOOLEAN_TYPE
2525
const booleanResult = (xpathResult) => xpathResult.booleanValue
2626

27-
let nodes = []
28-
const document = cy.state('window').document
29-
let iterator = document.evaluate(selector, document)
30-
31-
if (isNumber(iterator)) {
32-
const result = numberResult(iterator)
33-
Cypress.log({
34-
name: 'xpath',
35-
message: selector,
36-
$el: nodes,
37-
consoleProps () {
27+
const isPrimitive = (x) =>
28+
Cypress._.isNumber(x) || Cypress._.isString(x) || Cypress._.isBoolean(x)
29+
30+
// options to log later
31+
const log = {
32+
name: 'xpath',
33+
message: selector,
34+
}
35+
36+
const getValue = () => {
37+
let nodes = []
38+
const document = cy.state('window').document
39+
let iterator = document.evaluate(selector, document)
40+
41+
if (isNumber(iterator)) {
42+
const result = numberResult(iterator)
43+
log.consoleProps = () => {
3844
return {
3945
'XPath': selector,
4046
type: 'number',
4147
result
4248
}
43-
},
44-
})
45-
return result
46-
}
49+
}
50+
return result
51+
}
4752

48-
if (isString(iterator)) {
49-
const result = stringResult(iterator)
50-
Cypress.log({
51-
name: 'xpath',
52-
message: selector,
53-
$el: nodes,
54-
consoleProps () {
53+
if (isString(iterator)) {
54+
const result = stringResult(iterator)
55+
log.consoleProps = () => {
5556
return {
5657
'XPath': selector,
5758
type: 'string',
5859
result
5960
}
60-
},
61-
})
62-
return result
63-
}
61+
}
62+
return result
63+
}
6464

65-
if (isBoolean(iterator)) {
66-
const result = booleanResult(iterator)
67-
Cypress.log({
68-
name: 'xpath',
69-
message: selector,
70-
$el: nodes,
71-
consoleProps () {
65+
if (isBoolean(iterator)) {
66+
const result = booleanResult(iterator)
67+
log.consoleProps = () => {
7268
return {
7369
'XPath': selector,
7470
type: 'boolean',
7571
result
7672
}
77-
},
78-
})
79-
return result
80-
}
73+
}
74+
return result
75+
}
76+
77+
try {
78+
let node = iterator.iterateNext()
79+
80+
while (node) {
81+
nodes.push(node)
82+
node = iterator.iterateNext()
83+
}
84+
85+
log.consoleProps = () => {
86+
return {
87+
'XPath': selector,
88+
}
89+
}
8190

82-
try {
83-
let node = iterator.iterateNext()
91+
return nodes
92+
} catch (e) {
93+
console.error('Document tree modified during iteration', e)
8494

85-
while (node) {
86-
nodes.push(node)
87-
node = iterator.iterateNext()
95+
return null
8896
}
89-
} catch (e) {
90-
console.error('Document tree modified during iteration', e)
97+
}
9198

92-
return null
99+
const resolveValue = () => {
100+
return Cypress.Promise.try(getValue).then(value => {
101+
return cy.verifyUpcomingAssertions(value, options, {
102+
onRetry: resolveValue,
103+
})
104+
})
93105
}
94106

95-
// TODO set found elements on the command log?
96-
Cypress.log({
97-
name: 'xpath',
98-
message: selector,
99-
$el: nodes,
100-
consoleProps () {
101-
return {
102-
'XPath': selector,
103-
}
104-
},
107+
return resolveValue().then((value) => {
108+
// TODO set found elements on the command log?
109+
Cypress.log(log)
110+
if (isPrimitive(value)) {
111+
return value
112+
}
113+
return Cypress.$(value)
105114
})
106115

107-
return Cypress.$(nodes)
116+
108117
}
109118

110119
Cypress.Commands.add('xpath', xpath)

0 commit comments

Comments
 (0)