Dollar range

The exact price text

Imagine we want to confirm that the displayed price is within certain range, for example between $10 and $20. We need to grab the text of the element, remove the $character, convert the text to a number, then assert it is within the range. We could add a custom assertion, or simply construct a callback function to be passed as theshould(callback) argument to ensure the command plus assertion retries.

<div id="price">$99.99</div>
<script>
  // the initial price will come down after a discount
  // is applied to be within the expected range
  setTimeout(function () {
    document.getElementById('price').innerText = '$14.99'
  }, 1000)
</script>
function dollarRange(min$, max$) {
  // check the input arguments first
  if (min$ > max$) {
    throw new Error(`min $ should be <= max $`)
  }

  // construct the "should(callback)" function
  // that checks the given jQuery element
  return function checkElement($el) {
    const text = $el.text().replace('$', '')
    const price = Number(text)
    // use any Chai assertions inside the callback
    expect(price, 'price').to.be.within(min$, max$)
  }
}
// retry until the price element shows the
// price in the expected range, or the command times out
cy.get('#price').should(dollarRange(10, 20))

Includes the dollar amount text

Let's say the element has some text, including a dollar amount. We want to confirm the dollar amount is within a certain range.

<div id="shipping">Ground shipping for $11.79, up to 2 kg.</div>
function includePrice(min$, max$) {
  // check the input arguments first
  if (min$ > max$) {
    throw new Error(`min $ should be <= max $`)
  }

  return function ($el) {
    // named capture group that matches "$" + dollar + cents text
    const priceRe = /\$(?<price>\d+\.\d{2})/
    const text = $el.text()
    const match = text.match(priceRe)
    expect(match, 'matched price regex').not.to.equal(null)
    // convert the string to a number
    // and confirm it is within the given range
    const price = parseFloat(match.groups.price)
    expect(price, 'price').to.be.within(min$, max$)
  }
}

// confirm the element shows somewhere the price
// between $10 and $15
cy.get('#shipping').should(includePrice(10, 15))

You can also write the above test using (mostly) Cypress v12 queries in a single chain, finished by a single be.within assertion to confirm the extracted number is between min and max numbers.

// named capture group that matches "$" + dollar + cents text
const priceRe = /\$(?<price>\d+\.\d{2})/
cy.contains('#shipping', priceRe) // yields jQuery
  .invoke('text') // yields text
  .invoke('match', priceRe) // yields match
  .its('groups.price') // yields text
  .then(Number) // yields a number
  .should('be.within', 10, 15) // number is between X and Y

Only the cy.then(Number) breaks the retries, but you can replace it with cypress-mapopen in new window cy.apply query.

cy.contains('#shipping', priceRe)
  .invoke('text')
  .invoke('match', priceRe)
  .its('groups.price')
  .apply(Number) // cy.apply from cypress-map
  .should('be.within', 10, 15)