Discover more from Cypress Testing Tips & Tricks
Parse the URL search params
Advent day 21: How to confirm search part of the current browser URL
Sometimes your test navigates to a long URL like https://domain/path/to/page/?some=search¶ms=123#anchor
and you want to confirm the important part of the location. There is the cy.location command, but not enough people know that it can take an argument to return just a part of the location object.
cy.location().should(loc => {
expect(loc.search).to.equal('?some=search¶ms=123')
})
// equivalent
cy.location('search')
.should('equal', '?some=search¶ms=123')
The equality assertion is very strict. What if we are interested in confirming the `some=search` part? We could use the “includes” assertion
cy.location('search')
.should('include', 'some=search')
That is better, but what if you want to check multiple arguments? Can you convert the above URL search parameters into a plan object to be checked using the normal assertions like “have.keys”, “deep.include”, and “deep.equals”?
Yes, by using the browser’s own URLSearchParams API. Just pass the “search” part of the location to create a new instance of the object. The instance has its own API for getting the individual values which you can use via the cy.invoke command
cy.location('search')
.should('equal', '?search=value&id=1234')
// let's parse the search value
.then((s) => new URLSearchParams(s))
.invoke('get', 'id') // check a specific key
.should('equal', '1234')
Can you get a plain object from the search string? Yes, but that involves a few steps, you need to iterate over the “key / value” pairs to make an array and convert the array to a plain object using the Lodash _.fromPairs
function. I suggest you do this inside a should(callback)
function to enable the Cypress command retries.
cy.location('search').should((search) => {
const parsed = new URLSearchParams(search)
const pairs = Array.from(parsed.entries())
const plain = Cypress._.fromPairs(pairs)
expect(plain, 'search params').to.deep.equal({
search: 'value',
id: '1234',
})
})
Tip: the above example comes from my Cypress Examples location page. You can find several hundred tested Cypress command examples and recipes there.
I suggest you factor out the conversion from the search string to a plain object into a utility function and use it from every test that verifies the URL search parameters.
function searchToPlain() {
const parsed = new URLSearchParams(search)
const pairs = Array.from(parsed.entries())
const plain = Cypress._.fromPairs(pairs)
return plain
}
cy.location('search').should((search) => {
const plain = searchToPlain(pairs)
expect(plain, 'search params').to.deep.equal({
search: 'value',
id: '1234',
})
})
If you are already using the cypress-should-really library, you can construct the above helper on the fly, or as a reusable data transformation.
import {
construct, invoke, pipe, really
} from 'cypress-should-really'
// reusable function for converting search params to an object
const searchToPlain = pipe(
// string like "?foo=bar&baz=qux"
construct(URLSearchParams), // URLSearchParams
invoke('entries'), // Iterable<[string, string]>
Array.from, // Array<[string, string]>
Cypress._.fromPairs, // { [key: string]: string })
)
it('parses the URL search part', () => {
cy.visit(...)
cy.location('search')
.should(
really(searchToPlain, 'deep.equal', {
foo: 'bar',
baz: 'qux'
})
)
})
Nice: the pipe
takes all individual unary functions and creates a pipeline function that just waits for the data from the cy.location(‘search’)
command. The result is passed to the really
function which runs the assertion “deep.equal”. If an error is thrown, the cy.location
is run again, retrying until it times out or passes.
Subscribe to Cypress Testing Tips & Tricks
Tips and tricks about testing all the web things using Cypress.io