Collect All URLs Visited During Cypress Test

How to keep the list of all URLs visited during an end-to-end test.

Imagine you have a simple end-to-end test

  • visit the page
  • find a link and click on it
  • confirm the browser goes to the target page

Using Cypress I could write something like this

1
2
3
4
5
cy.visit('/')
cy.contains('a', 'videos').click()
cy.location('pathname').should('eq', '/videos.html')
cy.go('back')
cy.location('pathname').should('eq', '/')

What URLs did the test visit? Let's find out.

🎁 You can find the full source code for this blog post in the repo bahmutov/cypress-url-example.

Overwrite cy.visit

We could focus on the pages explicitly visited by the test runner by overwriting the cy.visit command and keeping track of all target urls. We can keep track of URL strings by storing an array in the Cypress.env object.

cypress/e2e/overwrite-visit.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Cypress.Commands.overwrite(
'visit',
(originalFn, url, options = {}) => {
Cypress.env('visitedUrls').push(url)
return originalFn(url, options)
},
)

beforeEach(() => {
Cypress.env('visitedUrls', [])
})

afterEach(() => {
console.log(Cypress.env('visitedUrls'))
})

it('visits multiple urls', () => {
cy.visit('/')
cy.contains('a', 'videos').click()
cy.location('pathname').should('eq', '/videos.html')
cy.go('back')
cy.location('pathname').should('eq', '/')
})

The finished test shows the ['/'] list with a single URL

Only the initial visit url is shown

Our test goes to the /videos.html URL via the click, not via cy.visit, and so it is not in the list stored in the Cypress.env('visitedUrls') array.

The test has visited the videos page

We need to store all urls, not just the starting ones.

Cypress events

Cypress has a long list of emitted events the test can subscribe to: uncaught:exception, window:confirm, etc. The event url:changed is the one we want to subscribe to.

cypress/e2e/url-changed.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
beforeEach(() => {
Cypress.env('visitedUrls', [])
Cypress.on('url:changed', (url) => {
// remove the base url
url = url.replace(Cypress.config('baseUrl'), '/')
Cypress.env('visitedUrls').push(url)
})
})

afterEach(() => {
console.log(Cypress.env('visitedUrls'))
})

it('visits multiple urls', () => {
cy.visit('/')
cy.contains('a', 'videos').click()
cy.location('pathname').should('eq', '/videos.html')
cy.go('back')
cy.location('pathname').should('eq', '/')
})

Now we see the list of the URLs that includes the initial page, followed by the videos page, back to the initial page.

All visited pages

Hmm, the list includes the starting page twice. Let's use a Set instead of an array.

cypress/e2e/spec.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
beforeEach(() => {
Cypress.env('visitedUrls', new Set())
Cypress.on('url:changed', (url) => {
// remove the base url
url = url.replace(Cypress.config('baseUrl'), '/')
Cypress.env('visitedUrls').add(url)
})
})

afterEach(() => {
const set = Cypress.env('visitedUrls')
console.log(set.values().toArray())
})

it('visits multiple urls', () => {
cy.visit('/')
cy.contains('a', 'videos').click()
cy.location('pathname').should('eq', '/videos.html')
cy.go('back')
cy.location('pathname').should('eq', '/')
})

Unique visited pages

Print to the terminal

Let's print the list of collected URLs to the terminal instead of the browser console. I will use my plugin cypress-log-to-term that prints the current subject to the terminal

1
2
3
4
5
afterEach(() => {
cy.wrap(Cypress.env('visitedUrls').values().toArray(), {
log: false,
}).log()
})

Tip: the afterEach hook executes even if the test fails, so you should always see the list of URLs.

In the terminal you should see the list of strings

1
["/","/videos.html"]

Plus the subject should be reflected in the Command Log

Logging the collected URLs to the Command Log and terminal

Nice.