Set "Puppeteer_Skip_Chromium_Download" Env Variable To Skip Download.

  • Read the docs
  • Run Cypress on your ain CI
  • Record success and failure videos
  • Move common code into utility package
  • Separate tests into bundles
  • Brand JavaScript crashes useful
  • Apply examination names when creating data
  • Get test condition
  • Explore the environment
  • Run all spec files locally
  • Get command log on failure
  • Wait on the right thing
  • Write and read files
  • Read JSON files with retries
  • Conditional logic
  • Customize Cypress examination runner colors
  • Shorten assertions
  • Disable ServiceWorker
    • Alternative: delete from each window object
  • Control navigator.language
  • Employ fixtures to stub network requests
  • Use code coverage
  • Use visual testing
  • Interactive and headed mode
  • Produce high quality video recording
  • Use the page base url
  • Laissez passer the environment variables correctly
  • Parse and utilise URL
  • Deal with target=_blank
  • Deal with window.open up
  • Bargain with window.location.replace
  • Minimize memory use
  • Kickoff browser with specific time zone
  • Scaffold the new projects faster
  • Install Chromium via Puppeteer
  • Create aliases before each test
  • Hover control
  • Wait for data
  • Make HTTP requests
  • Restart the tests
  • Expect for network idle
  • Check if the network call has not been made
  • Observe good Cypress examples
  • Use cy.log to print to the Command Log
    • Log mistake before throwing it
  • Use a single cy.contains control
  • Disable Save Credit Menu prompt
    • Turning the credit card salvage prompt off by changing the placeholder text
    • Turning the credit card salvage prompt off past using autocomplete off
  • Print timestamp with the mistake message
  • Add a custom delay command for better videos
  • Visit the blank page to stop the running app
  • Do non load pesky 3rd political party script
  • Bargain with blazon module
  • Access a MySQL database
  • Prepare Create-React-App ESLint warnings
  • Use Lodash
  • Detect when the document reloads
  • Fourth dimension function of the test
  • Departure between cy and Cypress globals
  • Compare ii loaded files
  • See besides
  • Related posts

We have been successfully using Cypress to verify our websites and web apps for a while. Here are some tips & tricks we have learned.

Even this blog is tested using Cypress :)

Read the docs

Cypress has splendid documentation at https://docs.cypress.io/, including examples, guides, videos and even a busy Gitter conversation channel. There is likewise "Search" box that quickly finds relevant documents. The search is really good and covers the docs, the weblog posts and the examples.

Recently common recipes were combined into a single place. If you still have an result with Cypress, please search through open up and airtight bug.

You can also read my other blog posts about Cypress nether tags/cypress.

Run Cypress on your own CI

As long as the projection has a projection key, you tin can run the tests on your ain CI boxes. I institute running within Docker to be the easiest, and even built an unofficial Docker image with all dependencies.

Annotation that cypress ci ... will upload recorded videos and screenshots (if capturing them is enabled) to the Cypress cloud, while cypress run ... will not.

Record success and failure videos

When Cypress team has released the video capture characteristic in version 0.17.11 it speedily became my favorite. I have configured our GitLab CI to go along artifacts from failed test runs for 1 calendar week, while keeping videos from successful test runs only for a iii days.

                    ane                    
2
3
4
5
6
vii
8
9
ten
11
12
13
                                          artifacts:                                        
when: on_failure
expire_in: 'one week'
untracked: true
paths:
- cypress/videos
- cypress/screenshots
artifacts:
when: on_success
expire_in: '3 days'
untracked: truthful
paths:
- cypress/screenshots

Whenever a examination fails, I lookout the failure video next with the video from the last successful test run. The differences in the subject nether test are quickly obvious.

Movement mutual code into utility parcel

Testing lawmaking should be engineered as well as the product code. To avoid duplicating testing helper utilities, nosotros advise to move all common helpers into their own NPM module. For example, we use local CSS styles with randomized class names in our web code.

                    1                    
2
3
                                          <div                        class="table-1b4a2 people-5ee98">                                        
...
</div>

This makes the selectors very long since we need to use prefixes.

                    1                    
                    cy.get('[class*=people-]')                                        

By creating a tiny helper function nosotros alleviated the selector head aches.

                    one                    
2
3
                                          import                      {classPrefix}                      from                      '@team/cypress-utils/css-names'                                        

cy.get(classPrefix('people'))

The exam lawmaking became a lot more than readable.

Split tests into bundles

Using a single cypress/integration/spec.js file to examination a large web application quickly becomes difficult and time consuming. We have separated our code into multiple files. Additionally, we keep the source files in src folder and build the multiple bundles in cypress/integration using kensho/multi-cypress tool.

The multi-cypress tool assumes that Docker and GitLab CI are used to really run the tests. Each test will exist inside its own "exam job", thus 10 spec files can exist executed at in one case (assuming at least x GitLab CI runners are available).

The command to run a specific spec file is cypress run --spec "spec/filename.js" by the way.

Make JavaScript crashes useful

As I take said before, the truthful value of a exam is non when it passes, but when it fails. Additionally, even a passing test might generate client-side errors that are not crashes and do not neglect the test. For example, we often use Sentry exception monitoring service where we forrad all client-side errors.

If a client-side error happens while the E2E Cypress test is running, we need this additional context: which test is executing, what steps were already finished before the fault was reported, etc.

Past loading a small library send-exam-info before each test, we can capture the name of the examination, its spec filename and fifty-fifty the Cypress exam log every bit breadcrumbs; this information is then sent to Sentinel with each crash. For example, the exception below shows the name of the test when the fault was reported

Test name

Similarly, the Cypress examination steps are logged as breadcrumbs and are sent with the exception assuasive any developer to quickly get a sense when the fault happens as the test runs.

Test steps

Employ test names when creating data

Often equally part of the E2E test we create items / users / posts. I like to put the total exam title in the created data to easily meet what test created which datum. Within the test we can quickly take hold of the full title (which includes the full parent's title and the current test proper noun) from the context.

I also like creating random numbers for additional departure

                    ane                    
ii
three
4
5
6
7
8
nine
10
11
12
                                          const                      uuid =                      () =>                      Cypress._.random(0,                      1e6)                    
describe('foo', () => {


it('bar', office () {
const id = uuid()
const testName = this.exam.fullTitle()
const proper noun = `exam name: ${testName} - ${id}`

makeItem({proper name})
})
})

Get test status

If yous use function () {} as the test'south body, or a torso of a hook, you tin can find lots of interesting backdrop in the this object. For instance, you can see the country of the test: passed or failed. To see them all, place the debugger keyword and run the test with DevTools open.

                    1                    
two
3
4
                    afterEach(                        office                        ()                      {                    
console.log(this)
debugger
})

There is an object for the unabridged test, and for the current hook

Information in the test context object

The this.currentTest object contains the status of the entire exam

Check if the test has passed or failed

Explore the environment

You tin pause the exam execution past using debugger keyword. Brand sure the DevTools are open!

                    1                    
2
3
4
                    it('bar',                                              function                        ()                      {                    
debugger

})

Run all spec files locally

If you separate Cypress E2E tests into multiple spec files, you soon terminate up with lots of files. If yous need to run the scripts one past 1 from the control line, I wrote a small utility run-each-cypress-spec.

                    i                    
2
three
4
5
6
vii
8
                    npm install -g run-each-cypress-spec                    
run-specs
...
running cypress/integration/a-spec.js
Started video recording: /Users/gleb/cypress/videos/p259n.mp4
...
running cypress/integration/b-spec.js
Started video recording: /Users/gleb/cypress/videos/62er1.mp4

If you need environment variables (similar urls, passwords), you lot can hands inject them using as-a

                    1                    
                    as-a cy run-specs                    

Get command log on failure

While videos of the failed tests are useful, sometimes we just want to get a sense of the failure before triaging it. In the headless CI style, you lot can become a JSON file for each failed exam with the log of all commands. See the JSON file below as an example of a failed exam and the file it generated.

                    1                    
2
3
4
5
6
7
8
nine
10
11
12
xiii
xiv
15
xvi
                    {                    
"testName": "Website loads the Almost tab",
"testError": "Timed out retrying: Expected to find content: 'Join Us' but never did.",
"testCommands": [
"visit",
"new url https://www.visitor.com/#/",
"contains a.nav-link, About",
"click",
"new url https://www.company.com/#/about",
"hash",
"assert expected **#/nigh** to equal **#/well-nigh**",
"contains Join Us",
"assert expected **body :not(script):contains(**'Bring together Us'**),
[type='submit'][value~='Join Usa']** to exist in the DOM"
]
}

This is a split projection that just needs to exist included from your cypress/support/alphabetize.js file. For instructions see cypress-failed-log projection.

Wait on the right thing

Information technology is of import to wait for the right element. Imagine we have a listing of items in the DOM. We expect a new element to appear with text "Hullo". Nosotros could select first the list, then the element containing "Hi" and check if it becomes visible

                    one                    
2
three
                    cy.get('.listing')                    
.contains('li', 'Hello')
.should('be.visible')

Simply sometimes it does not work. When we cy.get('.list') Cypress saves the DOM element as the "subject" and then tries to wait for a child of that chemical element with text "How-do-you-do" to become visible. If we utilise React for case, the DOM of the list might be recreated from scratch if nosotros push a new item into the list. When that happens Cypress notices that the "subject" DOM it holds a reference to becomes "discrete from the DOM" - it becomes an orphan!

A improve solution to this problem is to utilize a composite CSS selector that will grab the list AND the particular in a single operation.

                    1                    
2
                    cy.contains('.listing li',                      'Hi')                    
.should('be.visible')

This forces Cypress to wait for the listing AND listing item element without caching a reference to the DOM element .list.

You can use advanced CSS selectors to get an element in a single control. For example instead of cy.get('.something').outset() you lot could utilise cy.get('.something:outset-child'). Similarly,

                    1                    
2
3
4
5
vi
7
8
ix
                                        
cy.become('.something').beginning()
cy.get('.something:first-child')

cy.get('.something').last()
cy.get('.something:concluding-child')

cy.get('.something').eq(i)
cy.get('.something:nth-kid(two)')

Write and read files

Y'all tin can write files to deejay direct from Cypress using cy.writeFile and read an existing file using cy.readFile. What if you want to read a file that might not be? cy.readFile will fail the examination if file does not exist, thus we demand to detect a work around.

In my case, the file I would like to load is a JSON of test values useful for Jest-like snapshot testing.

Here is a cracking flim-flam. Salve the file using cy.writeFile, for example cy.writeFile('spanshots.json') whenever there is something to save. When reading the file, fetch it through the Cypress code proxy using fetch Notice how the test spec itself is served by Cypress from URL which looks like http://localhost:49829/__cypress/tests?p=cypress/integration/spec.js-438. Allow usa "hack" it to load a file that might not exist.

The lawmaking below tries to load snapshots file before each test. If the fetch phone call fails, the file does not be.

                    1                    
2
3
4
5
6
7
8
9
ten
eleven
12
                                          permit                      snapshots                    
beforeEach( function loadSnapshots() {
return fetch('/__cypress/tests?p=./snapshots.json')
.and so( r => r.text())
.and then( function loadedText(text) {

})
.catch( err => {
console.mistake('snapshots file does not be')
snapshots = {}
})
})

If the fetch call succeeds, the returned text will Non be original JSON! Instead information technology volition be webpacked module :)

For instance, here is the saved file

snapshots.json
                    one                    
2
iii
4
five
6
7
                    {                    
"spec.js": {
"works": [
"foo"
]
}
}

Here is how born Cypress bundler returns it in response to /__cypress/tests?p=./snapshots.json - I take shortened the webpack boilerplate preamble.

                    1                    
2
iii
iv
5
vi
7
eight
ix
                    (                        office                        e(t,n,r){                        part                        ...)({ane:[function(crave,module,exports){                                                              
module.exports={
"spec.js": {
"works": [
"foo"
]
}
}
},{}]},{},[1]);

Notice that our JSON has been placed into module with id 'one' (first line expression {1:). The entire bundle registers a stand alone require function. If nosotros want to get into the actual "module" contents we have to do the following in our function loadedText(text) to terminate the hack

                    one                    
2
3
4
v
vi
7
8
                                                                  part                        loadedText                        (text)                      {                    
if (text.includes('BUNDLE_ERROR')) {

return Promise.turn down(new Error('not constitute'))
}
const req = eval(text)
snapshots = req('i')
}

Nail, the object snapshots has been loaded from the file snapshots.json which might or might not be.

Notation while I have successfully used the above hack when running Cypress locally, it was always declining when doing cypress run in headless mode.

Read JSON files with retries

Cypress cy.readFile command automatically parses JSON files. It also re-reads the file if the assertions on the returned JSON neglect. For example, let'southward validate the number of items stored by the app in its JSON "database" file:

                    1                    
2
iii
4
v
6
7
viii
nine
ten
xi
12
13
14
                                        

cy.become('.new-todo')
.blazon('todo A{enter}')
.blazon('todo B{enter}')
.type('todo C{enter}')
.blazon('todo D{enter}')
cy.readFile('./todomvc/data.json').should( information => {
await(data).to.have.property('todos')
look(data.todos).to.have.length(4, '4 saved items')
await(information.todos[0], 'first item').to.include({
title: 'todo A',
completed: imitation
})

Fifty-fifty if the awarding sends the items after a time filibuster, the cy.readFile(...).should(cb) combination retries and successfully passes when all four items are institute.

Test passes after the file has been updated

Conditional logic

Sometimes you might need to collaborate with a folio element that does not always exist. For instance there might a modal dialog the showtime fourth dimension you use the website. You want to close the modal dialog.

                    one                    
2
3
4
v
                    cy.become('.greeting')                    
.contains('Close')
.click()
cy.go('.greeting')
.should('non.be.visible')

Only the modal is non shown the second time around and the to a higher place code will fail. In social club to check if an element exists without asserting it, use the proxied jQuery function Cypress.$

                    1                    
2
3
4
5
6
7
8
9
                                          const                      $el = Cypress.$('.greeting')                    
if ($el.length) {
cy.log('Endmost greeting')
cy.get('.greeting')
.contains('Shut')
.click()
}
cy.get('.greeting')
.should('not.be.visible')

The to a higher place cheque is safe.

Customize Cypress test runner colors

Read Cypress Halloween Theme and bank check out cypress-dark plugin.

Shorten assertions

For cy.location you tin can laissez passer the part yous are interested in

                    1                    
ii
three
                    cy.location().should(                        location                        =>                      {                    
expect(location.pathname).to.eq('/todos');
});

is the same as

                    1                    
                    cy.location('pathname').should('equal',                      '/todos')                    

You can use its to become nested properties easier

                    ane                    
2
3
                    cy.go('@login').should(                        req                        =>                      {                    
expect(localStorage.getItem('token')).to.be.eq(req.response.body.accessToken);
});

is the same every bit

                    one                    
2
                    cy.get('@login').its('response.trunk.accessToken')                    
.should( accessToken => expect(accessToken).to.equal(localStorage.getItem('token')))

and this could exist expressed more conspicuously in steps

                    1                    
ii
3
4
v
six
                    cy.then(() =>                      {                    

const token = localStorage.getItem('token')

cy.get('@login').its('response.body.accessToken').should('equal', token)
})

You tin can expect for network request to happen and then check something using cy.then

                    one                    
2
iii
four
5
                                        

cy.get('@login').should( req => {
expect(localStorage.getItem('token')).to.exist.null;
});

Instead we can use .so later on waiting for the network request. It is besides helpful to print the message when checking for the token, otherwise command log simply says "look nix to equal null".

                    1                    
ii
3
four
                    cy.await('@login')                    
.then(() => {
expect(localStorage.getItem('token'), 'token in local storage').to.be.null;
});

You don't need to wrap elements just to dynamically employ them. For example if you lot want to test each detail from the list

                    one                    
two
three
4
v
6
7
eight
9
ten
xi
                                          const                      inputs = [                    
{ element: 'username', text: 'foo', error: 'Please enter your username.' },
{ element: 'password', text: 'foo', error: 'Delight enter your countersign.' }
];
cy.wrap(inputs).each( input => {
const { chemical element, text, mistake } = input;

cy.getByTestId(chemical element)
.blazon(text)
.should('take.value', text);
})

Every Cypress command is automatically inserted into the queue, so yous can iterate over the items and utilize Cypress commands, everything will be queued correctly.

                    1                    
two
3
iv
5
6
vii
                    inputs.forEach(                        input                        =>                      {                    
const { element, text, error } = input;

cy.getByTestId(element)
.type(text)
.should('have.value', text);
})

I prefer cy.offset() to cy.eq(0).

Disable ServiceWorker

ServiceWorkers are great - but they can really impact your end-to-end tests past introducing caching and coupling tests. If you want to disable the service worker caching you need to remove or delete navigator.ServiceWorker when visiting the folio with cy.visit.

Beginning, here is the mode that does not work - simply deleting the belongings from the navigator object.

                    1                    
2
3
4
five
six
vii
eight
                    it('disables it',                                              function                        ()                      {                    
cy.visit('index.html', {
onBeforeLoad (win) {
delete win.navigator.ServiceWorker

}
})
})

The reason for this is that we are deleting the holding from the wrong object. The ServiceWorker is NOT divers on the navigator, it is defined on its prototype. You can confirm this by using Object.getOwnPropertyDescriptor method:

                    one                    
2
3
4
                                          Object.getOwnPropertyDescriptor(win.navigator,                      'ServiceWorker')                    

Object.getOwnPropertyDescriptor(win.navigator.__proto__, 'ServiceWorker')

Afterward we delete the property from the correct object, the application code will no longer recollect it can annals the service worker.

cypress/integration/spec.js
                    1                    
2
3
iv
5
six
7
                    it('disables it',                                              office                        ()                      {                    
cy.visit('index.html', {
onBeforeLoad (win) {
delete win.navigator.__proto__.ServiceWorker
}
})
})
index.html
                    one                    
2
iii
4
v
6
7
8
9
                                          <body>                                        
<script>
if ('ServiceWorker' in navigator) {
console.log('registering service worker')
} else {
console.log('skipping sw registration')
}
</script>
</torso>

Service worker is no longer registered

Annotation: once deleted, the SW stays deleted in the window, even if the application navigates to another URL.

Alternative: delete from each window object

Y'all can register an issue handler to fire on every window load issue, where you can remove the ServiceWorker

                    ane                    
2
3
4
                    Cypress.on('window:earlier:load',                      (win) =>                      {                    

delete win.navigator.__proto__.ServiceWorker
})

Read also "Stub navigator API in cease-to-stop tests" for another example.

Control navigator.language

Imagine you take a page that shows different greeting depending on the navigator.linguistic communication property.

                    ane                    
two
3
4
5
six
7
8
nine
ten
11
12
                                          <body>                                        
<div id="greeting"> </div>
<script>
const greeting = document.getElementById('greeting')
if (navigator.language === 'Klingon') {

greeting.innerText = 'nuqneH'
} else {
greeting.innerText = 'Hi there'
}
</script>
</trunk>

How do we check if the default English greeting is displayed? Easily

                    1                    
2
3
4
                    information technology('shows default greeting',                      () =>                      {                    
cy.visit('index.html')
cy.contains('#greeting', 'Hello there').should('be.visible')
})

But how practise we force the Klingon greeting to exist displayed? Trying to change read-merely navigator.language holding throws an error.

                    ane                    
ii
iii
4
5
half dozen
7
8
9
                    it('shows Klingon greeting',                      () =>                      {                    
cy.visit('index.html', {
onBeforeLoad (win) {

win.navigator.language = 'Klingon'
}
})
cy.contains('#greeting', 'nuqneH').should('be.visible')
})

Error is thrown when trying to directly set `navigator.language`

The navigator.language holding is actually gear up on navigator.__proto__ object:

                    1                    
2
three
4
                                          Object.getOwnPropertyDescriptor(navigator,                      'language')                    

Object.getOwnPropertyDescriptor(navigator.__proto__, 'language')

Nosotros can create language holding on the navigator object instead - thank you, prototypical inheritance! We tin specify the belongings's value:

                    i                    
2
3
4
5
vi
7
8
nine
10
eleven
12
thirteen
14
xv
16
                    it('shows Klingon greeting',                      () =>                      {                    
cy.visit('index.html', {
onBeforeLoad (win) {






Object.defineProperty(win.navigator, 'language', {
value: 'Klingon'
})
}
})
cy.contains('#greeting', 'nuqneH').should('exist.visible')
})

This test shows proper respect to each Klingon warrior browsing the spider web.

Klingon greeting

How practice nosotros ensure that the awarding actually read navigator.language belongings when displaying the greeting? Maybe the "nuqneH" is hard-coded! We demand to runway navigator.language "go" access. Nosotros can exercise this using cy.stub method.

                    1                    
2
3
four
5
half dozen
7
8
9
10
xi
                    it('checks if awarding gets language holding',                      () =>                      {                    
cy.visit('index.html', {
onBeforeLoad (win) {
Object.defineProperty(win.navigator, 'language', {
get: cy.stub().returns('Klingon').as('language')
})
}
})
cy.contains('#greeting', 'nuqneH').should('be.visible')
cy.get('@linguistic communication').should('have.been.calledOnce')
})

Now nosotros know for sure how the application behaves.

Language was read by the application

Use fixtures to stub network requests

If your tests are full of stubbed network responses, motility the responses into fixtures

before

                    1                    
2
3
4
5
6
seven
8
9
10
eleven
12
xiii
fourteen
15
16
17
xviii
nineteen
xx
21
22
23
                    information technology('does A',                      () =>                      {                    
cy.server()
cy.route({
method: 'Mail service',
url: '**/api/v1/suggestion_query/query',
response: {
... loooong response object ...
}
})

})

information technology('does B', () => {
cy.server()
cy.route({
method: 'Mail service',
url: '**/api/v1/suggestion_query/query',
response: {
... another loooong response object ...
}
})

})

after 1

                    i                    
two
3
4
5
6
seven
eight
9
10
11
12
13
14
15
xvi
17
18
nineteen
20
21
                    it('does A',                      () =>                      {                    
cy.server()
cy.road({
method: 'POST',
url: '**/api/v1/suggestion_query/query',

response: 'fixture:first_query_response'
})

})

it('does B', () => {
cy.server()
cy.route({
method: 'Post',
url: '**/api/v1/suggestion_query/query',

response: 'fixture:second_query_response'
})

})

after two

You tin fifty-fifty require the JSON fixtures directly if the 2d response is simply a petty bit different from the first response

                    one                    
2
3
4
five
vi
7
eight
ix
10
11
12
13
14
15
16
17
18
xix
20
21
22
23
24
                                          const                      responseFixture =                      require('../fixtures/first_query_response')                    
it('does A', () => {
cy.server()
cy.route({
method: 'Mail service',
url: '**/api/v1/suggestion_query/query',
response: responseFixture
})

})

it('does B', () => {


const secondResponse = Cypress._.cloneDeep(responseFixture)
secondResponse.someProperty = 'new value'
cy.server()
cy.route({
method: 'Post',
url: '**/api/v1/suggestion_query/query',
response: secondResponse
})

})

See cy.route and cy.fixture documentation.

Bonus: read Import Cypress fixtures

Utilise code coverage

Yous tin can instrument your application and employ Cypress code coverage plugin to produce combined reports in multiple formats. Terminate-to-end tests are very effective at covering a lot of lawmaking in a single exam.

  • Combined Stop-to-end and Unit of measurement Test Coverage

Use visual testing

If a test grows to be very long because information technology checks so many elements on the page, see if information technology makes sense to test the entire page using Visual Testing.

before

                    1                    
two
3
four
5
6
7
eight
ix
10
eleven
12
xiii
fourteen
xv
                    it('checks lots of things',                      () =>                      {                    


cy.become('selector1').should('be.visible')
.find('sub-selector1').should('have.text', 'expected text')
cy.get('selector2').should('be.visible')
.observe('sub-selector2').should('have.text', 'expected text')
cy.get('selector3').should('be.visible')
.observe('sub-selector3').should('take.text', 'expected text')
cy.get('selector4').should('be.visible')
.observe('sub-selector4').should('have.text', 'expected text')
cy.go('selector5').should('be.visible')
.detect('sub-selector6').should('have.text', 'expected text')
...
})

afterwards

                    1                    
2
3
4
v
6
7
8
nine
10
11
12
13
                    it('checks lots of things',                      () =>                      {                    



cy.go('selector1').should('be.visible')
.find('sub-selector1').should('have.text', 'expected text')





cy.takeVisualSnapshot('user activity')
})

Bonus: check out bahmutov/sudoku for visual component testing using open source tools, and read Visual testing for React components using open source tools.

Interactive and headed mode

You can notice out if the test is currently running using the interactive mode which is when the user calls cypress open.

                    1                    
2
3
4
v
                                          if                      (Cypress.config('isInteractive')) {                    

} else {

}

When you use the interactive mode yous always see the browser, thus the browser is always headed. But when using the cypress run command, the browser might exist headless or headed. You tin find out how the browser is displayed:

                    1                    
2
3
4
5
half-dozen
vii
                                          if                      (Cypress.browser.isHeaded) {                    



} else {

}

When using cypress run, Electron is headless by default, while Chrome and Firefox browsers are headed by default. You can control the browser though:

                    1                    
                    npx cypress run --browser chrome --headless                    

Trying to pass both --headed and --headless CLI parameters raises an mistake.

Produce high quality video recording

Read the full blog mail Generate High-Resolution Videos and Screenshots.

You can detect well-nigh of the advice below implemented in bahmutov/cypress-picture. To generate better videos from tests

  • set up video compression to false
  • ready browser window size to larger value, which should work fine when using headless Chrome on CI
                    1                    
ii
3
4
5
6
7
eight
9
                                        
on('before:browser:launch', (browser = {}, launchOptions) => {
if (['chrome', 'chromium'].includes(browser.name) && browser.isHeadless) {
launchOptions.args.push(
`--window-size=1920,1080`,
)
}
return launchOptions
})
  • hide command log

Yous can "expand" the application under test iframe to embrace the entire window

                    one                    
two
3
4
v
6
vii
8
9
ten
xi
12
13
14
fifteen
sixteen
17
18
nineteen
xx
21
22
23
24
                                          const                      clearViewport =                      () =>                      {                    
const runnerContainer = window.parent.document.getElementsByClassName(
'iframes-container',
)[0]
runnerContainer.setAttribute(
'style',
'left: 0; meridian: 0; width: 100%; height: 100%;',
)

const sizeContainer = window.parent.document.getElementsByClassName(
'size-container',
)[0]
sizeContainer.setAttribute('style', '')

const sidebar = window.parent.document.getElementsByClassName(
'reporter-wrap',
)[0]
sidebar.setAttribute('style', 'opacity: 0')

const header = window.parent.document.querySelector(
'.runner.container header',
)
header.setAttribute('style', 'opacity: 0')
}

Call this function before the tests.

  • utilize native Chrome Debugger Protocol to take full folio screenshots. See "cypress-picture" project.

Utilise the page base of operations url

Sometimes multiple tests visit a different base url. For example, you might normally get to the index page by setting baseUrl in the config file

cypress.json
                    one                    
two
3
                    {                    
"baseUrl": "http://localhost:7080"
}

Every test can cy.visit('/') and get to the alphabetize folio. Only imagine that a suite of tests is trying to visit and test the "Near" page. It is located at /about.html, and then every test has to visit it.

                    ane                    
two
3
4
5
vi
7
8
nine
10
11
                    describe('About',                      () =>                      {                    
it('loads', () => {
cy.visit('/most.html')
.contains('h1', 'About')
})

it('loads again', () => {
cy.visit('/near.html')
.contains('h1', 'About')
})
})

Visiting the about page from every test

Now, we tin refactor the tests to avoid duplication. For example, we could movement the cy.visit into beforeEach claw.

                    1                    
ii
iii
4
5
half dozen
seven
viii
ix
ten
11
12
13
14
15
                                        

depict('About', () => {
beforeEach(() => {
cy.visit('/near.html')
})

information technology('loads', () => {
cy.contains('h1', 'About')
})

it('loads again', () => {
cy.contains('h1', 'About')
})
})

Works.

Or nosotros could create a utility function:

                    1                    
2
3
iv
5
6
7
8
9
x
xi
12
thirteen
                    describe('About',                      () =>                      {                    
const visit = () => cy.visit('/virtually.html')

information technology('loads', () => {
visit()
cy.contains('h1', 'About')
})

it('loads again', () => {
visit()
cy.contains('h1', 'Near')
})
})

It works as well.

Hither is one more way to (corruption) test configuration feature introduced in Cypress v5.0.0. We volition alter the baseUrl in the describe block to apply to merely these tests.

                    1                    
2
iii
4
five
6
7
8
9
10
11
                    describe('Virtually', {                      baseUrl:                      '/about.html'                      },                      () =>                      {                    
it('loads', () => {
cy.visit('')
cy.contains('h1', 'About')
})

it('loads again', () => {
cy.visit('')
cy.contains('h1', 'Most')
})
})

Detect the { baseUrl: '/near.html' } parameter in describe call. Nosotros can overwrite multiple config values using this parameter. In our case, we overwrite the baseUrl, appending the file name. In the cy.visit('') call we pass an empty string - because nosotros exercise non want to suspend / at the end.

Merely the tests fail.

Extra slash at the end fails the tests

While our visit used an empty cord, Cypress requires baseUrl to exist a valid base URL and appends / at the end automatically.

Luckily, Cypress v5.iv.0 has a fix for baseUrl that has params for outcome #2101. If your base of operations url is of the grade ...?foo=bar then visiting '' would keep the original full url. Permit's modify our test by appending ? to the base url:

                    1                    
ii
                                          - describe('Nearly', { baseUrl: '/about.html' }, () => {                                        
+ describe('About', { baseUrl: '/almost.html?' }, () => {

Now the tests pass - and the ?/ at the end of the URL is ignored

The page is visited correctly

Pass the environment variables correctly

Imagine you demand to pass database username and password from your test. These values are probably available as surroundings variables. How would y'all pass them to cypress run? You lot might try something like this in the bundle.json file. It volition NOT work:

package.json
                    1                    
two
3
4
5
                    {                    
"scripts": {
"cy:run": "node_modules\\.bin\\cypress run --env db.user='$DB_USERNAME',db.countersign='$PASSWORD'"
}
}

Starting time, you do not need the node_modules\\.bin path when calling an NPM alias - they are automatically resolved past the npm run command. Thus ever use simply:

package.json
                    ane                    
ii
3
4
5
                    {                    
"scripts": {
"cy:run": "cypress run --env db.user='$DB_USERNAME',db.password='$Countersign'"
}
}

The above control will Non work still. To see why, change the cypress run to cypress open to see the Cypress GUI.

package.json
                    i                    
2
3
4
5
half dozen
                    {                    
"scripts": {
"cy:run": "cypress run --env db.user='$DB_USERNAME',db.password='$Countersign'",
"cy:open up": "cypress open --env db.user='$DB_USERNAME',db.password='$PASSWORD'"
}
}

Select the "Settings" tab and inspect the resolved surround variables.

Resolved environment variables

We have two problems:

  1. Instead of passing the environment variable'due south value, nosotros got the strings "$DB_USERNAME" and "$Password". Resolving surround variables within commands might exist catchy and depend on the operating system.
  2. The username and the password are stored under names db.user and db.password, which might be not what you expect. From the dot notation, I would expect these values to create an object db with properties user and countersign.

But there are other trickier problems here. Let'south change our NPM scripts to avoid single quotes around the values. Nosotros hope that now the environment variables are resolved correctly.

package.json
                    1                    
two
3
4
5
6
                    {                    
"scripts": {
"cy:run": "cypress run --env db.user=$DB_USERNAME,db.password=$PASSWORD",
"cy:open": "cypress open up --env db.user=$DB_USERNAME,db.password=$PASSWORD"
}
}

At first, information technology appears to work

The username and the password were passed correctly

Now, allow's try a user name with a space.

The username is incorrect, the password is missing completely

Ummm. The infinite inside the username value created bug; the cypress open command finer received ended with --env db.user=joe smith,db.password=123. The part after --env db.user=joe was ignored!

Solution: Cypress can grab the surround variables in multiple means. The well-nigh powerful and flexible way is to grab them from the process.env object using the plugins code.

cypress/plugins/index.js
                    ane                    
ii
3
iv
5
6
7
8
9
10
11
12
thirteen
xiv
fifteen
xvi
17
18
19
twenty
21
                                          module.exports =                      (on, config) =>                      {                    
const username = process.env.DB_USERNAME
const password = process.env.PASSWORD

if (!username) {
throw new Error(`missing DB_USERNAME environment variable`)
}

if (!password) {
throw new Error(`missing PASSWORD environment variable`)
}



config.env.db = {
username, password,
}


return config
}

The plugins file runs in Node environment, has access to the process.env object. It tin can catch the variables, check their values, and identify them into a single object db. Now the Settings tab shows the right values.

The right "db" object with the username and password

Additional reading: the blog post Go along passwords secret in E2E tests and the recipe Environs variables.

Tip: when working locally you can use the utility every bit-a to conveniently inject environment variables when running any control.

Parse and use URL

Permit's accept a Adjacent.js application with dynamic routes. We can scaffold one using the provided example.

You can notice the complete source code at bahmutov/dynamic-routing-app

The app was initialized using the provided example command

                    ane                    
                                          $                                              npx create-next-app --case dynamic-routing dynamic-routing-app                                        

The app contains two dynamic routes:

  1. pages/post/[id]/index.js
    • e.one thousand. matches /mail/my-example (/mail service/:id)
  2. pages/post/[id]/[comment].js
    • eastward.thou. matches /post/my-example/a-comment (/post/:id/:comment)

Let's run into how we can parse the above URLs to use them during tests. First we need to install Cypress and start-server-and-test:

I can scaffold the Cypress files with my helper bahmutov/cly

                    ane                    
                                          $                                              npx @bahmutov/cly init                                        

We place the base url localhost:3000 in cypress.json file and starting time the app and Cypress. Our offset test tin confirm the home page loads:

cypress/integration/spec.js
                    1                    
ii
3
4
5
6
vii
8
9
                    describe('Dynamic routes',                      () =>                      {                    
information technology('loads home', () => {
cy.visit('/')
cy.contains('li', 'Dwelling house')
cy.contains('li', 'About')
cy.contains('li', 'First Post')
cy.contains('li', 'Second Post')
})
})

Home test

Bang-up, we can make sure the link "Nearly" goes to the "Most" folio.

                    1                    
2
three
4
                    it('goes to the about folio',                      () =>                      {                    
cy.visit('/').contains('li a', 'Well-nigh').click()
cy.url().should('match', /\/about$/)
})

The test passes, simply with a tiny red flag. Find piffling eye crossed icons side by side to the "contains" command?

The link was invisible when clicked

The Next.js application fetches a static HTML with the markup present but invisible. It is hydrated later - but nosotros desire to click on the link similar a real user would, after it becomes visible. Let's add an assertion.

                    1                    
two
3
4
                    it('goes to the well-nigh page',                      () =>                      {                    
cy.visit('/').contains('li a', 'About').should('be.visible').click()
cy.url().should('friction match', /\/about$/)
})

Much meliorate.

The link becomes visible and then is clicked

We assert that nosotros go to the correct folio by using the expression cy.url().should('match', /\/well-nigh$/). It works, but the command cy.url returns the full URL. If you lot click on the control yous will encounter what information technology yields:

cy.url yields the full application URL

Nosotros are interested only in the relative pathname - the /about part. Thus I suggest using cy.location command that parses the URL and tin can yield only the interesting part.

Now let'south bank check the first post. We can outset with the same exam as earlier

                    one                    
ii
3
iv
                    it('goes to the first post',                      () =>                      {                    
cy.visit('/').contains('li a', 'Showtime Post').should('be.visible').click()
cy.location('pathname').should('equal', '/mail/beginning')
})

Testing the first post

At present permit's get to the first command. Nosotros need to click on the link, but before we do this, allow's validate it. Let'due south grab the current post url and use it somehow in our tests.

                    i                    
2
3
4
v
six
seven
                    it('goes to the showtime comment',                      () =>                      {                    
cy.visit('/').contains('li a', 'Showtime Postal service').should('be.visible').click()
cy.location('pathname').should('include', '/post')
.and then( pathname => {
console.log('pathname is', pathname)
})
})

Later on the assertion .should('include', '/mail') passes, the value is yielded to the next command where we print it.

Getting the pathname after the page navigation

Now we have all the JavaScript magic we need to parse and piece URL. Allow'due south even modify the test and go to the 2nd comment of the kickoff post.

                    1                    
two
three
4
five
half dozen
7
viii
9
10
11
12
13
xiv
15
16
17
18
19
xx
21
22
23
                    it('goes to the first post / second comment',                      () =>                      {                    
cy.visit('/').contains('li a', 'Start Postal service').should('be.visible').click()
cy.location('pathname')
.should('include', '/post')
.then( pathname => {


const [, , post] = pathname.split('/')
look(post, 'first post').to.equal('first')


const commentUrl = `/post/${post}/2d-comment`
cy.go(`a[href="${commentUrl}"]`).should('exist.visible').click()
})


cy.location('pathname').should( pathname => {

const [, , post, annotate] = pathname.split('/')
expect(post, 'postal service id').to.equal('first')
await(comment, 'comment id').to.equal('second-annotate')
})
})

We can parse, validate, and use the URL in our test.

Using parsed URL to browse to the second comment

Deal with target=_blank

Imagine a link on the page uses <a href="/about.html" target="_blank">About</a>. Cypress does not piece of work with the second tab, and so what can we do?

                    one                    
2
iii
4
                    information technology('loads the about page',                      () =>                      {                    
cy.visit('index.html')
cy.get('a').click()
})

Test opens the second tab invisible to Cypress

Figure out what you desire to test. You can for example verify the link has the expected address and aspect target=_blank and call it a day.

                    1                    
2
three
4
5
half-dozen
7
                    it('loads the about page',                      () =>                      {                    
cy.visit('index.html')
cy.get('a').should($a => {
wait($a.attr('href'), 'href').to.equal('/most.html')
await($a.attr('target'), 'target').to.equal('_blank')
})
})

Test confirms the anchor link's attributes

We can also change the target attribute earlier clicking the link (but after confirming information technology to be blank). And then we tin verify the second "tab" loads correctly.

                    1                    
2
3
iv
v
6
vii
8
ix
                    it('loads the about folio',                      () =>                      {                    
cy.visit('index.html')
cy.get('a').should($a => {
expect($a.attr('href'), 'href').to.equal('/about.html')
await($a.attr('target'), 'target').to.equal('_blank')
$a.attr('target', '_self')
}).click()
cy.location('pathname').should('equal', '/about.html')
})

Test opens the target link in the same tab

Bargain with window.open up

Imagine the awarding under test uses window.open up() to load a new URL in the second tab.

                    i                    
2
3
4
5
half dozen
7
                                          <a                        href="/most.html"                        target="_blank">Virtually</a>                                        
<script>
document.querySelector('a').addEventListener('click', (e) => {
e.preventDefault()
window.open up('/most.html')
})
</script>

Past default the /virtually.html page opens in a new tab - and nosotros do non want that. Let's stub the window.open method instead.

                    ane                    
2
3
4
five
6
seven
viii
                    it('opens the about page',                      () =>                      {                    
cy.visit('index.html')
cy.window().and so( win => {
cy.stub(win, 'open up').every bit('open')
})
cy.get('a').click()
cy.become('@open').should('have.been.calledOnceWithExactly', '/about.html')
})

Test stubs the window.open method

If we really want to load the new URL, let'southward call the original window.open method, passing _self as the second parameter.

Tip: use <method>.wrappedMethod to get to the original method wrapped in Sinon stub. When nosotros call cy.stub(win, 'open') it replaces win.open up method with a Sinon stub function. If we desire to call the real win.open up method, we tin can use the reference to the saved original part: the win.open up.wrappedMethod is the original unstubbed win.open up method.

                    i                    
ii
3
4
v
six
7
8
9
10
11
12
thirteen
                    it('opens the about page',                      () =>                      {                    
cy.visit('index.html')
cy.window().then( win => {
cy.stub(win, 'open').callsFake((url, target) => {
expect(target).to.be.undefined


return win.open.wrappedMethod.telephone call(win, url, '_self')
}).as('open')
})
cy.become('a').click()
cy.become('@open').should('have.been.calledOnceWithExactly', '/about.html')
})

Test redirects `window.open` target

See also: the post Stub window.open and video cy.stub: stub a method on the window object

Deal with window.location.supersede

Imagine, our index.html does the following:

                    1                    
two
3
4
5
six
7
viii
9
                                          <trunk>                                        
<h1>First folio</h1>
<script>
setTimeout(() => {

window.location.replace('https://www.cypress.io')
}, 2000)
</script>
</trunk>

The redirect causes problems, as we cannot admission the new origin from the examination. We cannot simply stub the location.supervene upon method - this property (and pretty much every belongings on the location object) is locked downwards; it is neither writable, nor configurable.

The `window.location` property descriptors

Thus we cannot apply cy.stub(win.location, 'supersede'). We cannot supervene upon window.location property either - considering it also is locked down. Instead we need to modify our application's code to avoid using the window.location.supersede method completely.

During the test, we tin utilise the cy.intercept command to modify the application lawmaking. For instance, it could telephone call window.__location.supplant instead. Our exam would create this dummy window.__location object earlier the application loads.

cypress/integration/spec.js
                    1                    
2
3
4
5
6
seven
eight
ix
10
11
12
xiii
14
15
16
17
xviii
19
twenty
21
                                        

information technology('replaces', () => {
cy.on('window:before:load', (win) => {
win.__location = {
supervene upon: cy.stub().every bit('supercede')
}
})

cy.intercept('Get', 'alphabetize.html', (req) => {
req.keep( res => {
res.torso = res.body.replaceAll(
'window.location.replace', 'window.__location.replace')
})
}).as('index')

cy.visit('alphabetize.html')
cy.wait('@index')
cy.contains('h1', 'Outset page')
cy.go('@replace').should('have.been.calledOnceWith', 'https://www.cypress.io')
})

The test runs and passes. Notice in that location is no redirect, and our stub and intercept were called.

The passing test

You tin can inspect the certificate the browser receives afterward the intercept replaced the window.location with window.__location string. The replacement happens at the proxy level, before the response is sent to the browser. See the presentation How cy.intercept works for details.

The HTML source the browser receives

You can watch the above caption in this video

Minimize memory use

Sometimes Cypress can crash during CI run due to running out of retentivity. Typically it shows a message like this:

                    ane                    
two
3
four
five
six
7
8
9
10
11
12
13
14
15
xvi
17
                    10:38:58 AM: We detected that the Chromium Renderer procedure just crashed.                    
ten:38:58 AM:
x:38:58 AM: This is the equivalent to seeing the 'distressing face' when Chrome dies.
10:38:58 AM:
10:38:58 AM: This can happen for a number of different reasons:
10:38:58 AM:
10:38:58 AM: - You wrote an countless loop and y'all must fix your own code
x:38:58 AM: - At that place is a memory leak in Cypress (unlikely but possible)
x:38:58 AM: - You are running Docker (there is an easy fix for this: run across link below)
10:38:58 AM: - You are running lots of tests on a memory intense awarding
10:38:58 AM: - You are running in a retentivity starved VM surround
10:38:58 AM: - At that place are problems with your GPU / GPU drivers
10:38:58 AM: - In that location are browser bugs in Chromium
10:38:58 AM:
10:38:58 AM: You can acquire more including how to set Docker here:
10:38:58 AM:
10:38:58 AM: https://on.cypress.io/renderer-process-crashed

You can contour memory usage every 2nd with environment variables

                    one                    
ii
3
                    export DEBUG=cypress:server:util:process_profiler                    
export CYPRESS_PROCESS_PROFILER_INTERVAL=1000
cypress run

The things you can do to minimize the amount of memory used:

  • try running tests without extra reporters, plugins, and preprocessors
  • split specs to have fewer tests per spec. Cypress opens and closes the browser for every spec
  • turn the video recording off with video: false using cypress.json or surround variables
  • plow the command log off using the environment variable CYPRESS_NO_COMMAND_LOG=one
  • expose garbage collection and running it subsequently each test. Come across issue 8525, but in general for Electron browser you want to apply an environment variable ELECTRON_EXTRA_LAUNCH_ARGS=--js-flags=--expose_gc and from the test call window.gc() method if available
                    1                    
two
iii
iv
5
6
7
8
9
10
11
12
                    afterEach(() =>                      {                    
cy.window().so( win => {
if (win.gc) {
gc();
gc();
gc();
gc();
gc();
cy.wait(1000)
}
})
})

Tip: if Electron keeps crushing, try running tests using Chrome or Firefox browsers.

Get-go browser with specific time zone

Y'all can outset Cypress (and thus the browser information technology spawns) with specific time zone to meet how the application handles information technology.

                    1                    
2
3
                    $ <timezone> npx cypress open                    
# for instance
$ TZ=Asia/Tokyo npx cypress open up

Typical time zones are America/New_York, Europe/Berlin, Europe/London, Asia/Tokyo.

Tip: an application tin can obtain its time zone using the following code snippet

                    1                    
2
                                          Intl.DateTimeFormat().resolvedOptions().timeZone                    

For more information, read Testing Time Zones in Parallel

Scaffold the new projects faster

Past default every new projection scaffolds case specs when you run cypress open for the very start time. I have written an utility @bahmutov/cly to scaffold a sample project without opening Cypress and and so deleting the many scaffolded spec files.

Yous yet need to install Cypress equally a dev dependency starting time, only then y'all can do

                    1                    
2
                    npm i -D cypress                    
npx @bahmutov/cly init

Y'all tin scaffold a TypeScript or a bare-bones projects

                    i                    
2
3
4
                    # adds appropriate tsconfig.json                    
npx @bahmutov/cly init --typescript
# uncomplicated unmarried spec without any fixtures, plugins, back up files
npx @bahmutov/cly init --blank

Watch me scaffolding new projects in the video below

Install Chromium via Puppeteer

Sometimes the CI machine you lot want to run tests on does non take Chrome installed, merely you really desire to use it to run your tests. For example, Chrome is more stable and crashes less often than Electron. If you cannot install Chrome browser using "normal" commands, you can install Chromium past installing Puppeteer NPM dependency.

                    1                    
two
3
                    npm i -D puppeteer                    
## or use Yarn
yarn add -D puppeteer

From the plugin file we can observe the browser and insert it into the list of other arrangement browsers discovered by Cypress.

cypress/plugins/index.js
                    1                    
ii
3
iv
5
vi
7
8
9
10
11
12
13
14
xv
16
17
18
19
20
21
22
23
24
25
26
                                          const                      puppeteer =                      require('puppeteer')                    

module.exports = async (on, config) => {
const browserFetcher = puppeteer.createBrowserFetcher()
const revisions = await browserFetcher.localRevisions()
if (revisions.length <= 0) {
console.fault('Could not discover local Chromium browser')

return
}
const info = await browserFetcher.revisionInfo(revisions[0])
console.log('found Chromium %o', info)

const chromium = {
name: 'chromium',
family: 'chromium',
displayName: 'Chromium',
version: info.revision,
majorVersion: info.revision,
path: info.executablePath,
channel: 'dev'
}
config.browsers.push(chromium)

return config
}

Open Cypress and you lot should meet "Chromium" in the drop down list of browsers.

Chromium browser in the drop down list

Tip: if you have problems with Cypress browser detection, run it with DEBUG=cypress:server:browsers environs variable.

To pick the Chromium browser in headless mode use the control:

                    i                    
2
iii
                    npx cypress run --browser chromium --headless                    
## or using Yarn
yarn cypress run --browser chromium --headless

See my example project cypress-chromium-via-puppeteer-example.

Tip: if yous need to skip installing Puppeteer in some circumstances, apply the environment variable PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true. I utilize this variable on some CIs to avoid waiting for the Chromium to download if I do not plan to run E2E tests in Chromium.

Create aliases earlier each test

If you lot create aliases using .every bit command, remember that aliases are reset before each test. Thus y'all cannot create them in the before hook and use from the second test:

                    ane                    
2
iii
4
5
6
7
8
9
ten
eleven
12
13
fourteen
                    before(() =>                      {                    
cy.wrap('some value').as('exampleValue')
})

it('works in the showtime test', () => {
cy.go('@exampleValue').should('equal', 'some value')
})


it('does not be in the 2nd test', () => {


cy.become('@exampleValue').should('equal', 'some value')
})

Instead, create the aliases using beforeEach hook

                    ane                    
two
3
4
v
half dozen
seven
8
9
10
11
12
                    beforeEach(() =>                      {                    

cy.wrap('some value').as('exampleValue')
})

information technology('works in the first test', () => {
cy.go('@exampleValue').should('equal', 'some value')
})

information technology('works in the 2nd test', () => {
cy.get('@exampleValue').should('equal', 'some value')
})

Hover control

Meet bahmutov/cy-hover-example for links.

Wait for data

Tip: come across the tested case at cypress-examples "Wait for data" recipe

Imagine we have a variable that after will receive some data. How do we retry the check from the test? Imagine we are waiting for a specific network request, and once it arrives it saves the list sent by the awarding.

Let me bear witness you what will not piece of work:

                    1                    
2
3
4
5
half-dozen
vii
8
nine
10
11
                                          let                      listing                    

cy.intercept('POST', '/users', (req) => {
if (Array.isArray(req.body)) {
list = req.body
}
})

cy.become('#post-list').click()

look(list).to.be.an('array')

The above code does not work because the expect(list).to.be.an('array') runs earlier the cy.get('#post-list').click() executes. The expect is immediate, while cy.get is chained and delayed.

You might delay checking the list by moving it into .and then callback to be executed after the .click() command:

                    1                    
2
3
four
5
half dozen
seven
8
9
x
11
12
13
                                          let                      list                    

cy.intercept('POST', '/users', (req) => {
if (Array.isArray(req.body)) {
list = req.body
}
})

cy.become('#mail-list').click()
.then(() => {

expect(list).to.be.an('assortment')
})

The in a higher place code is slightly more than right, but is still wrong. It will bank check the value of the list variable After clicking on the push, but without car-retries, it will only check once. What are the chances that the cy.intercept worked past then? Pretty much nil.

We need to retry checking the list, which we can do by changing the .then to .should command. The .should(cb) is retried automatically. Thus our showtime solution is to use the cy.should(callback) command:

                    i                    
ii
iii
iv
5
vi
seven
8
nine
10
11
12
13
xiv
15
sixteen
17
18
19
20
21
22
                                          let                      listing                    

cy.intercept('Postal service', '/users', (req) => {
if (Array.isArray(req.trunk)) {
list = req.body
}
})

cy.go('#post-list').click()
.should(() => {




expect(list).to.exist.an('assortment')
})
.then(() => {

render list
})

.should('accept.length', ii)

You can see the solution working in the recording below, where the awarding requests the data subsequently about i.3 second delay.

Waiting for data using cy.should command

We can ameliorate this solution a footling scrap past using cy.wrap control. We can wrap a reference to an object, and the intercept volition set up a property inside that object. This "trick" allows us to use implicit assertions completely bypassing the need for ".so" callbacks.

                    1                    
2
3
four
five
half-dozen
7
8
9
10
xi
12
thirteen
xiv
xv
xvi
17
18
19
20
21
22
                                        

const data = {}

cy.intercept('Mail', '/users', (req) => {
if (Assortment.isArray(req.torso)) {
data.listing = req.torso
}
})

cy.get('#post-list').click()



cy.wrap(data)


.should('have.property', 'listing')

.should('be.an', 'array')

.and('have.length', 2)

Waiting for data using cy.wrap command

Brand HTTP requests

Read Cypress asking and cookies

Restart the tests

You lot can programmatically click the "Re-run" tests button from code to restart the tests

                    1                    
                                          window.meridian.document.querySelector('.reporter .restart').click()                    

Come across cypress-grep and cypress-picket-and-reload examples.

Expect for network idle

Cypress is simply JavaScript, and with the new cy.intercept you tin can implement your own "wait for the network to be idle for Due north seconds" characteristic.

                    1                    
two
3
iv
5
half-dozen
vii
8
9
10
eleven
12
13
14
15
16
17
18
nineteen
20
21
                    information technology('waits for network to exist idle for 1 second',                      () =>                      {                    
let lastNetworkAt
cy.intercept('*', () => {
lastNetworkAt = +new Date()
})

cy.visit('/?delay=800')


const started = +new Date()
cy.wrap('network idle for one sec').should(() => {
const t = lastNetworkAt || started
const elapsed = +new Appointment() - t
if (elapsed < 1000) {
throw new Mistake('Network is busy')
}
})


cy.get('.todo-list li', { timeout: ten }).should('have.length', 2)
})

I take moved the above helper into a plugin cypress-network-idle. The above exam could be written as:

                    ane                    
2
                    cy.waitForNetworkIdle(1000)                    
cy.get('.todo-list li', { timeout: 10 }).should('have.length', 2)

Lookout man the videos introduction to Cypress-network-idle plugin and Ready Intercept And Wait Using cypress-network-idle Plugin.

Cheque if the network call has not been made

Here is a test that confirms a specific network call is Not fabricated until the application adds a new detail.

                    one                    
ii
3
4
5
6
seven
viii
nine
10
eleven
12
xiii
xiv
15
16
17
18
19
xx
21
22
23
                    it('does not make Post /todos request on load',                      () =>                      {                    


cy.intercept('Postal service', '/todos', cy.spy().as('mail'))
cy.visit('/')



cy.wait(yard)
cy.get('@mail').should('not.have.been.chosen')

cy.become('.new-todo').type('a new item{enter}')

cy.get('@post')
.should('have.been.calledOnce')


.its('args.0.0.torso')
.should('deep.include', {
title: 'a new detail',
completed: false
})
})

Find practiced Cypress examples

You can find good Cypress example repositories by searching GitHub using topic cypress-case and user bahmutov

                    1                    
                    github.com: topic:cypress-case user:bahmutov                    

Here is directly link to the results.

Similarly, yous tin can search for this topic under organisation name cypress-io

                    one                    
                    github.com: topic:cypress-example user:cypress-io                    

Here is the direct link to the results.

Use cy.log to print to the Command Log

You tin print the output of a command or a chore using the cy.log control. For example, lets print the object yielded from a task

                    i                    
                    cy.job('getImageResolution',                      'test-smile.png').then(cy.log)                    

If the object is big, cy.log shortens it and just prints Object{N} where N is the number of properties. We can print the unabridged object by passing it through a JSON.stringify method first.

                    one                    
two
3
                    cy.task('getImageResolution',                      'test-smile.png')                    
.so(JSON.stringify)
.then(cy.log)

Object printed using cy.log

Because cy.log returns void, it does not change the current discipline, and yields its statement to the next Cypress command.

                    1                    
two
3
four
5
6
                    cy.task('getImageResolution',                      'examination-smile.png')                    
.then(JSON.stringify)
.so(cy.log)
.and then(JSON.parse)
.should('include.all.keys', ['width', 'summit', 'filename', 'format'])
.and('have.holding', 'format', 'PNG')

Y'all tin watch this example in this short video

Log fault before throwing it

If you are making a asking and desire to log the possible errors, use .then + cy.log combination before an assertion.

                    one                    
2
three
4
5
half-dozen
7
8
9
x
11
                    cy.asking({ ... })                    
.its('body')
.and so((body) => {
if (body.errors) {

cy.log(JSON.stringify(body.errors, null, two))
}


})
.should('not.have.primal', 'errors')

Apply a single cy.contains command

Instead of using cy.get(selector).contains(text).should('be') concatenation, you lot can only apply

                    1                    
                    cy.contains(selector, text)                    
  1. The assertion should('exist') is already built into cy.contains command
  2. The unmarried command volition be retried until the text appears or the command times out

Watch this short video to see the single command in action.

Disable Salve Credit Card prompt

Sometimes when you fill a credit menu course during the examination, the Chrome browser asks if yous want to save the credit card numbers.

Save card browser prompt

Note: I have taken the credit menu example from this case and ran it locally.

In club to stop Chrome browser from ever asking to store payment methods, y'all could navigate to the special chrome://settings/payments URL and flip the switch:

The browser payment settings

Unfortunately, I could non find an equivalent Chrome command line switch to plough it off when launching the browser. Thus I needed another way.

Turning the credit card salve prompt off past changing the placeholder text

In my item example I institute that the browser did non show the "Relieve Credit Card" prompt if the place holder text did not include "credit" or "number" words.

                    1                    
ii
3
four
                    # shows the card salve prompt                    
placeholder="Card Number"
# does not show the menu salvage prompt
placeholder="Bill of fare Digits"

Unfortunately, you lot could not modify the placeholder attribute afterward the page has loaded. The test code below did not work:

                    1                    
ii
three
4
5
                                        


cy.go('[proper name=number]')
.invoke('attr', 'placeholder', 'Card digits')

Thus I have alter the application'southward HTML values.

Turning the credit card salvage prompt off by using autocomplete off

In another credit carte form example, I was able to successfully turn off the credit card save prompt by adding an attribute autocomplete=off on the form chemical element itself.

                    1                    
two
three
4
                                        

cy.go('course')
.invoke('attr', 'autocomplete', 'off')

Effort information technology - it might work for you.

Impress timestamp with the error message

Past default a failed test merely prints the error bulletin. You might desire to have the timestamp to meliorate debug the failure. The way to practice this is to modify the mistake message in the failure upshot handler. This is what I have done in Cypress v8:

                    i                    
2
three
4
5
6
                                        

Cypress.on('fail', (fault, runnable) => {
error.message = new Date().toUTCString() + '\north' + error.bulletin
throw fault
})

I am using the Cypress.on method instead of cy.on to make the handler apply to every examination. Here is the failing test

                    1                    
two
three
4
5
half-dozen
vii
eight
9
ten
11
                                          const                      f = {                    
failme() {
throw new Error('I am out!')
},
}

it('prints error with timestamp', () => {

cy.wait(2000)
cy.wrap(f).invoke('failme')
})

Hither is the last output

                    1                    
2
3
4
                                          1) prints error with timestamp:                    
Mistake: Tue, 27 Jul 2021 12:10:24 GMT
Timed out retrying later on 4000ms: I am out!
Error: Timed out retrying later 4000ms: I am out!

Add a custom delay command for meliorate videos

I ofttimes use the custom delay command to make the recorded test videos clear. Cypress runs pretty fast, and sometimes it is hard to empathise from the video what exactly has happening. So I oftentimes add a 1 2d delay before clicking a button or checking a box.

                    1                    
2
3
4
                    cy.get('button#submit')                    
.should('be.visible')
.await(yard, { log: false })
.click()

I extracted the .wait(m, { log: false }) into a custom command delay.

                    one                    
2
3
iv
5
vi
vii
viii
9
10
                                          const                      filibuster =                      (subject, ms =                          chiliad                        ) =>                      {                    
cy.wait(ms, { log: imitation })
if (subject) {
cy.wrap(subject, {
log: imitation,
})
}
}

Cypress.Commands.add('filibuster', { prevSubject: 'optional' }, filibuster)

The above control tin can be used in a chain or as a stand-alone parent command.

Visit the blank page to stop the running app

Run into the web log post Visit The Blank Page Betwixt Cypress Tests.

Practise not load pesky tertiary party script

Sometimes 3rd party scripts throw random errors when the page is irresolute quickly. In that case, stub the resource with an empty cord.

                    1                    
2
three
4
5
6
seven
eight
nine
ten
11
12
13
14
15
xvi
17
18
                                        


const doNotLoadPayPalJS = () => {
cy.log('**doNotLoadPayPalJS**')
cy.intercept(
{
method: 'Get',
hostname: 'www.paypal.com',
},
{
body: '',
},
)
}

doNotLoadPayPalJS()
cy.visit('/')

Deal with type module

If the project uses ES6 modules by specifying in the package.json "blazon": "module", this could cause issues for loading the Cypress plugin file. In that case, create a dummy package.json in the cypress folder. Specify the CommonJS blazon and Node will resolve the plugin file again.

cypress/bundle.json
                    1                    
2
3
4
5
                    {                    
"name": "cypress-tests",
"private": truthful,
"type": "commonjs"
}

See the example in bahmutov/verify-code-example.

Admission a MySQL database

The test runner could admission a database during the test to read some information. For case, the weblog post How To Verify Phone Number During Tests Part two describes how to query a MySQL database to read the phone number verification lawmaking and inbound it into the spider web application course.

Fix Create-React-App ESLint warnings

When creating a new application using Create-React-App, you lot volition run across ESLint warnings - because it does non know almost the Cypress globals like cy and Cypress.

ESLint gives a warning for cy global

To gear up, install Cypress ESLint plugin and add together the recommended settings to the bundle.json

packet.json
                    one                    
2
three
four
5
half dozen
vii
eight
                    {                    
"eslintConfig": {
"extends": [
"react-app",
"plugin:cypress/recommended"
]
}
}

For more than details, run into How to configure Prettier and VSCode.

Use Lodash

The super useful Lodash library is bundled with Cypress under Cypress._ belongings. Thus you do not demand to install information technology or even import information technology from the spec file.

                    one                    
2
                                          - import _ from 'lodash'                                        
+ const { _ } = Cypress

If you lot need Lodash methods in your plugin file, you will need to install and require it, though.

Discover when the document reloads

If the page submits a class, or navigates, sometimes the test needs to expect for the new certificate to load. To accomplish this, we tin can compare the certificate reference earlier the navigation and after.

                    1                    
two
3
4
5
6
                                        
cy.certificate().then((doc) => {

cy.contains('push button', 'create').click()
cy.certificate().should('not.equal', dr.)
})

The to a higher place code works, but the exclamation dumps the document object into the Cypress Command Log, which takes up a lot of infinite there. Improved version without a very long assertion in the Command Log can use assert part.

                    1                    
ii
3
iv
5
6
                                        
cy.document().then((doc) => {

cy.contains('push', 'create').click()
cy.document().should((d) => assert(d !== doc, 'document inverse'))
})

Fourth dimension function of the test

You can fourth dimension how long part of a exam takes, or even an individual Cypress command. Then you lot tin can add an explicit exclamation nearly the elapsed duration. For example, let's time how long the loading element in the application is visible. Nosotros tin can assert the loading element goes away in less than two seconds:

                    1                    
2
3
4
five
6
seven
8
9
10
11
12
13
14
15
16
17
xviii
19
xx
                                          allow                      started                    
cy.go('.loading')
.should('be.visible')
.then(() => {

started = +new Date()
})

cy.get('.loading')
.should('non.be.visible')
.then(() => {



const finished = +new Date()
const elapsed = finished - started
look(elapsed, 'loading takes less than 2 seconds').to.be.lessThan(
2000
)
})

See the full explanation in the video below:

Divergence between cy and Cypress globals

Cypress sets two global objects while running: cy and Cypress (if you desire to avert them, use local-cypress module). The cy object is only valid during the test execution; it tin can just be called inside a exam callback role or inside a hook function (like before, beforeEach, afterEach, and after). The cy object schedules commands like cy.visit, creates spies cy.spy, and sets aliases using cy.equally - all these operations only make sense in the context of a examination. If yous endeavor to phone call a cy method outside the test, Cypress throws an error.

The global Cypress object has static helper methods. It holds the static data virtually the spec file itself, the operating system, and the examination blazon.

                    i                    
ii
3
4
5
6
                    Cypress.arch                    
// "x64"
Cypress.platform
// "darwin"
Cypress.spec
// { accented: "...", name: "...", specType: "..." }

The global Cypress too has the bundled libraries, similar Cypress._ (Lodash), Cypress.$ (jQuery), Cypress.Hope (Bluebird), and a few others.

Finally, Cypress object has the configuration properties available at any time via Cypress.config method. All other data used during the test tin exist passed and stored in the Cypress.env method. You can call whatsoever Cypress method anywhere: from a test, outside the test, or even from DevTools panel.

                    ane                    
2
3
four
5
                                        
Cypress.env('greeting', 'hi')

console.log(Cypress.env('greeting'))

Watch the video Cypress vs cy Difference Explanation.

Compare two loaded files

Permit's say you want to compare two binary files using base of operations 64 encoding. You can use cy.readFile command and pass the file into the side by side stride using cy.then callback

                    ane                    
2
3
4
5
                    cy.readFile('first.png',                      'base64').and then(                        start                        =>                      {                    
cy.readFile('second.png', 'base64').and then( 2nd => {
expect(first, 'images are equal').to.equal(second)
})
})

The in a higher place assertion might be verbose because it tries to print the values in the comparison. You can minimize the control log assertion message by throwing an mistake just if the images are not equal

                    1                    
two
3
four
v
half dozen
7
8
                    cy.readFile('commencement.png',                      'base64').then(                        starting time                        =>                      {                    
cy.readFile('second.png', 'base64').then( 2d => {
if (first !== 2d) {
throw new Mistake('get-go prototype is unlike from the second')
}
cy.log('Two images are the same')
})
})

See likewise

  • my website cypress.tips
  • Writing a Custom Cypress Command and How to Publish Custom Cypress Command on NPM
  • Cypress weblog, I accept written a large number of web log posts at that place.
  • Notes of Best Practices for writing Cypress tests
  • The Hitchhikers Guide to Cypress End-To-End Testing
  • Unit testing Vuex information store using Cypress.io Test Runner
  • video playlist Cypress Tips & Tricks
  • Cypress web log posts by Filip Hric

  • NPM Tips and Tricks
  • Git Tips And Tricks
  • Large Spider web App Evolution

DOWNLOAD HERE

Posted by: doyounithe73.blogspot.com