Cypress: Mixed Topics


How to access the parameter passed in an open window event

$Tag.Cypress.Stubbing.OpenWindowEvent.AccessingPassedParameter

The following code shows how we can not only detect when a click would open a window but also access the parameter which was passed to the method that opens the window:

var passedParameter = ''
cy.on('window:before:load',
(win) => {
    cy.stub(win, 'open', (passedParam) => {
       passedParameter = passedParam;
  }).as('windowOpenAction');
});

cy.get(some_selector).click();

cy.get('@'windowOpenAction').then(() => {         
    cy.log(`The passed parameter: ${ passedParameter }`);
});

Implicit & Explicit Assertions

Implicit Assertions

Explicit Assertions

$Tag.Cypress.Assertions.Explicit

Many assertions in Cypress are done by .should() which asserts the object which was yielded to it by its parent command. But what if you don’t want to assert anything yielded by a parent command? In that case we can use explicit asserts.

Here is a self-explaining example:

var x = 5
expect(x).to.equal(6)

Here is another example using the contains comparison:

var s = ‘Today is a nice day’
expect(x).to.contain(‘nice’)

Using Node fs methods

$Tag.Cypress.FileSystemMethods.Node.fs

From within cypress its not possible to use Node fs methods by using code such as the following example:

var fs = require('fs');
var path = some file path
fs.unlinkSync(path);

If you would have such a code within your cypress script or in a js  file used by your cypress script you would get an error such as the following error when the cypress runner is executing your script:

fs.unlinkSync is not a function

To be able to use fs library and its methods you would have to use cy.task() method.

First you would have to make sure that the fs library and the fs library method you would like to use are included in the cypress/plugin/index.js file. Here is an example how this could be done:

cypress/plugin/index.js

const fs = require('fs')
...
module.exports = (on, config) => {
...
    on('task', {
        unlinkSync(filePath){
            fs.unlinkSync(filePath);
            return null
        }
    });
...
});

Then you could use that fs method with cy.task() as follows:

cy.task('unlinkSync', filePath)

Timeout

.should()

$Tag.Cypress.Timeout.”.should()”

The .should() command retries its previous command. Because of this we have two timeouts involved here. The first timeout is the timeout of the previous command which is retried by .should() and the second timeout defined how long the .should() command is supposed to perform those retries. Here is an example involving both timeouts:

cy.get(‘input’, { timeout: 10000 }).should(‘have.value’, 30)

Mocking and Stubbing

$Tag.Cypress.Mocking&Stubbing

Stubbing

We talk about stubbing if the tested application (e.g., web application being tested) initiates are real call to its external dependency but the external dependency is not the real one, but one simulated by the test framework.

In other words, stubbing allows us to test an application even if the external dependency does not really exist and is only simulated.

This approach allows to test an application in isolated, cut off from real external dependencies, which are simulated by the test framework and allow us as tester to see and test the behavior of the tested application in an isolated and controlled environment.

Cypress allows us to simulate API calls of the tested web application. It allows us to intercept the calls before they would reach the real API and send a response ourselves in place of the real API to the tested web application. This would allow us to test the web application even if the external API does not yet exist. We just simulate its behavior.

Handling Cookies

Reading and setting cookies

The following example shows how we can read a cookie and how we can set a cookie using all possible parameters:

var cookie1;

cy.getCookie('the-name-of-the-cookie')
.then((cookie)=>{
  cy.log(JSON.stringify(cookie));
  cookie1 = cookie;
}).as('cookie1Read');

// do something

cy.get('@cookie1Read').then( ()=> {
  cy.setCookie('new-name-of-the-cookie', cookie1.value, {
	domain: cookie1.domain, 
	path: cookie1.path, 
	expiry: cookie1.expiry, 
	httpOnly: cookie1.httpOnly,
	sameSite : cookie1.sameSite, 
	secure: cookie1.secure
  });
});

Cypress Hooks

Hooks on Cypress test script level

These are hooks that allow you to execute commands inside a Cypress test script. The before() and after() hooks allow you to execute commands before any and after all of the tests (it-blocks) within the Cyprss test script are executed. To execute commands before or after each individual test (it-block) within the Cypress test script the beforeEach() and afterEach() hooks can be used. More info on this can be found here:

https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Hooks

To execute commands for a specific Cypress test script before or after any of the individual tests (it-blocks) is executed you can, in addition to before() and after() hooks within your Cypress test script x.cy.js, also use the hooks on Node JS level see next section (Hooks on Node JS level).

Hooks on Node JS level

These are hooks that allow you to execute commands before or after a specific Cypress test execution (execution of one x.cy.js test) or before or after the whole run (involving all x.cy.js tests).

From Cypress 10 onward, these hooks are normally placed inside cypress.config.js.

The following example shows how we can use the hooks inside cypress.config.js which are executed on the Node JS level.

cypress.config.js:

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  experimentalInteractiveRunEvents: true,
  reporter: 'cypress-multi-reporters',
  reporterOptions: {
    reporterEnabled: 'cypress-mochawesome-reporter, mocha-junit-reporter',
    cypressMochawesomeReporterReporterOptions: {
      reportDir: 'cypress/reports',
      charts: true,
      reportPageTitle: 'My Test Suite',
      embeddedScreenshots: true,
      inlineAssets: true,
    },
    mochaJunitReporterReporterOptions: {
      mochaFile: 'cypress/results/test-results.[hash].xml',
      testCaseSwitchClassnameAndName: false,
    },
    video: true,
  },
  e2e: {
    setupNodeEvents(on, config) {
      on('before:run', (test, details) => {
        console.log("Before run hook");
	  });

      on('after:run', (test, runnable) => {
        console.log("After run hook")
      });
      on('after:spec', (spec, results) => {
        if (results && results.stats.failures !== 0) {
          console.log(`${spec.name}`);
        }
      })
    }
  }
})

Logging

Logging in headless mode

Normally if you run Cypress in the headless mode your cy.log() messages are not shown in the commands window output. This might not be an issue if you are working with Cypress dashboard only because the cy.log() messages appear there without any problems. But if you really need to see the logs in the commands window output, for example because you have a limited number of runs included in your Cypress subscription, here is a solution:

Add the following code to the cypress.config.ts (or cypress.config.js):

cypress.config.ts:

import { defineConfig } from "cypress";

export default defineConfig({
  component: {
    devServer: {
      framework: "next",
      bundler: "webpack",
    },
  },

  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
      on('task', {
        log(message) {
          console.log(message +'\n\n');
          return null;
        },
      });
    },
    experimentalSessionAndOrigin: true
  },
});

Then you can write logs from cypress test scripts using the cy.task() command as shown here:

describe("Dummy test", function () {
  var serverAddress = '';

  it("Logging test", function () {
      cy.task('log', 'Display some logging');
    });
  });
  
  export {}

Connecting Cypress Test to Dashboard

Go to https://dashboard.cypress.io/ and login to your Cypress account.

Select [Project settings] and write down the Project ID.

Open your local cypress.config.ts (or cypress.config.js in case of JavaScript files) file and enter the project ID:

import { defineConfig } from "cypress";

export default defineConfig({
  component: {
    devServer: {
      framework: "next",
      bundler: "webpack",
    },
  },
  projectId: 'project-ID-from-Cypress-Dashboard',
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
      ...
    },
    ...
  },
});

Then make sure that you run the cypress command with the record key of cypress dashboard:

cypress run –record –key the-key-from-cypress-dashboard

Customizing Cypress paths

You can customize the Cypress test script path in the file cypress.config.js or cypress.config.ts using the following approach that is applicable to Cypress versions 10 and higher:

cypress.config.ts example:

import { defineConfig } from "cypress";

export default defineConfig({
  ...
  e2e: {
    // The pattern used to match test files
    specPattern: '../some-path/cypress/e2e/*.cy.ts',
    ...
  },
});

You can also define custom folders where to look for test scripts directly on the “cypress run “command, see the following post on how to do this: Cypress: Running selected tests only

Cypress commands

cpyress open command

Launching Cypress from another path as the current path


The following example shows how we can be in any current path but execute Cypress from another specific path:


yarn install --cwd "$env:HOMEDRIVE$env:HOMEPATH\temp\cypress\"
$env:CYPRESS_serverAddress='http://localhost:5000/'
yarn --cwd "$env:HOMEDRIVE$env:HOMEPATH\temp\cypress\" cypress open --env KeyCloakAdminPass=$env:KEY_CLOAK_ADMIN_PASS,clearTenants=false