The site https://demoqa.com/ contains various challenges for people wanting to improve their skills in test automation.

As a part of monthly test automation workshops, we were challenged to develop tests in Cypress for a specific case under the https://demoqa.com/alerts address, which would test if, after clicking the second button from the top alert will appear after 5 seconds.

The most significant difference between solutions was in methods used to measure passing time. In this article, I will present those different approaches and describe how each method affected the results of the tests.

Part 1 – accessing alert

The major problem we encountered was accessing the alert that should appear after 5 seconds which is quite tricky when using Cypress.

We used cy.stub to deal with it.

it('counts 5 seconds', () => {
        cy.window().then((win) => {
       cy.stub(win, 'alert').as('alert')
     })

Part 2 measuring time

After dealing with the problem of accessing the alert, the next thing was to test if it actually appeared after 5 seconds after clicking the button.

The primary approach to making this test was the same in all cases.

  1. Make the 1st timestamp after clicking the button.
  2. Make the 2nd timestamp after the alert window appears.
  3. Subtract the value of the 1st timestamp from the 2nd to get the time passed after clicking the button.
  4. Make an assertion that will check if the value from step 3 is bigger than 5 seconds/5000 milliseconds.

The main difference was in which method each person used to measure time. Below is a quick summary of them, including code examples and the time they measured.

You may also like: How go around Cypress restrictions on two different superdomains?

Date.now()

it('Click second button, alert will appear after 5 seconds', () => {
       let startTime, alertTime;


       cy.window().then($win => {
           cy.stub($win, 'alert').as('alert');
       });
       cy.clickButton('Click me', 1).then(() => {
           startTime = Date.now();
       });
       cy.get('@alert').should('have.been.calledOnceWithExactly', 'This alert appeared after 5 seconds').then(() => {
           alertTime = Date.now() - startTime;
           expect(alertTime).to.be.within(4850, 5100);
           cy.log('Alert time: ' + alertTime + 'ms');
       });
   });

Results in milliseconds:

  1. 4965
  2. 4963
  3. 4968
  4. 4968
  5. 4971

new Date()

describe("Alert", () => {
 it("Should wait for alert", () => {
   cy.visit("/alerts", {
     onBeforeLoad(win) {
       cy.stub(win, "alert").as("alert");
     },
   });
   let timestamp;
   cy.get("#timerAlertButton").click().then(()=>{
     timestamp = new Date();
   })
  


   cy.get("@alert").should(
     "have.been.calledOnceWithExactly",
     "This alert appeared after 5 seconds"
   ).then(()=>{
     const loadTime = new Date();
     const actualTime = loadTime - timestamp;
     cy.log(actualTime);
     expect(actualTime).to.be.within(4750,5250);
   })
 });
});

Results in milliseconds:

  1. 4972
  2. 4957
  3. 4961
  4. 4957
  5. 4950

Performance.now()

describe('counter', () => {
   it('counts 5 seconds', () => {
        cy.window().then((win) => {
       cy.stub(win, 'alert').as('alert')
     })
  
       cy.visit('https://demoqa.com/alerts')
       cy.get('button[id=timerAlertButton]').then(() => {
         const t0 = performance.now()
  
       cy.get('button[id=timerAlertButton]').click()
      
         cy.on('window:alert', (text) => {
           expect(text).to.contains('This alert appeared after 5 seconds');
           const t1 = performance.now()
           const alertTime = ((t1 - t0)/1000)
           expect (alertTime).to.be.greaterThan(5)
           cy.log(`Alert appeared after ${alertTime} seconds.`);
         })
       })
       })
   });

Results in seconds:

  1. 5.073599999994039
  2. 5.0685
  3. 5.07609999999404
  4. 5.091200000017881
  5. 5.091400000005961

Comparison of results

As we can see in the case of using Date.now() and new Date() results were quite similar, but they were never above 5 seconds. So if the assertion is strict and requires the results to be bigger than 5 seconds, tests would not pass. But when we look at the results when using performance.now(), they are always above 5 seconds. So, in this case, the assertion would be positive.

What causes such difference is the point in time to which each method relates. Both Date.now() and newDate() is relative to the Unix epoch (1970-01-01T00:00:00Z) and dependent on the system clock. On the other hand, Performance.now() is relative to page load and relies on monotonic time. Therefore it is not affected by time adjustments that the system clock may be target off.

In conclusion, performance.now() is better suited for monitoring the time in which a piece of code was executed, which in the case described in this article is the time passed since clicking the button to the time the alert appears on the screen. Another benefit is that Performance.now() makes timestamps with precision up to microseconds.