This article is about fixing DOMexception Document is not focused in Cypress.io Cypress
  • 31-Jan-2023
Lightrun Team
Author Lightrun Team
Share
This article is about fixing DOMexception Document is not focused in Cypress.io Cypress

DOMexception: Document is not focused in Cypress.io Cypress

Lightrun Team
Lightrun Team
31-Jan-2023

Explanation of the problem

Test Case: A test case has been developed to validate the correct processing of text in the DOM to a text blob copied to the keyboard. The test uses the Cypress test framework and the following code to execute the tests:

describe('..., () => {
  before(() => {
    cy.visitPage();
  });

  it('should open the logs', () => {
    cy.get('body')
      .type('{ctrl}{shift}`');
    cy.get('[data-cy="stubs-fake-logs-content"]')
      .contains(<check text>)
      .click();
  });

  it('should click on a log line', () => {
    cy.get('[data-cy="actions-table"]')
      .find('tr')
      .eq(1)
      .click();
  });

  it('should copy the response content', () => {
    cy.get('[data-cy="copySnippetButton-JSON"]')
      .click();
  });

  it('should validate the correct data was extracted', () => {
    cy.task('getClipboard')
      .should('contain', '"<key>": "<value>"');
    cy.task('getClipboard')
      .should('not.contain', 'ResponseData');
  });
});

Clipboard handling: The button that is clicked has custom handling for the clipboard due to legacy browsers. The following code is used to handle the clipboard:

export const copyText = async text => {
  if (navigator.clipboard) {
    return navigator.clipboard.writeText(text);
  }

  const readOnlyTextArea = document.createElement('textarea');
  readOnlyTextArea.value = text;

  readOnlyTextArea.setAttribute('readonly', true);
  readOnlyTextArea.style.position = 'absolute';
  readOnlyTextArea.style.left = '-9001px';

  document.body.appendChild(readOnlyTextArea);

  readOnlyTextArea.select();
  document.execCommand('copy');

  document.body.removeChild(readOnlyTextArea);
};

Error: An error has been observed in the clipboard handler, where the following error message appears: Screenshot 2021-09-22 at 12 58 15. The tests may succeed several times, then suddenly fail on the next run with the above error, causing the assert on the clipboard contents to fail. This behavior is inconsistent and may take between 1-10 runs to show up. It does not appear in production settings.

Desired Behavior: Cypress should ensure that the application DOM/window stays in focus within its test-runner, such that the clipboard access functions and other DOM-related actions do not display behavior that is not seen in browsers.

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for DOMexception: Document is not focused in Cypress.io Cypress

The error message occurs in the Cypress test for a button that is meant to copy the selected value of a select component to the clipboard. The error message states that the test window has lost focus, which is confusing because it occurs in headless mode when the dev tools aren’t active. This error message can also be reliably reproduced when running Cypress interactively and opening the dev tools window.

The initial solution was to focus the actual test window before clicking the copy button, but that did not work. Despite verifying that the window was focused using cy.focused, the problem persisted. After some trial and error, the following solution was found to work:

cy.get('#copy-button').focus()
cy.realClick('#copy-button', { force: true })

Focusing the copy icon/button before clicking it with cy.realClick from the cypress-real-events plugin seems to fix the issue. Only using cy.focus in combination with cy.click or relying solely on cy.realClick does not have the desired effect. However, using the combination of the two, the error could not be reproduced in more than a dozen test runs. Although the solution works, it is still not a proper fix and the root cause of the problem needs to be addressed by the Cypress team.

Other popular problems with Cypress

Problem: Inconsistent Element Selection

Another common issue with Cypress is inconsistent element selection. This occurs when tests are not able to consistently select the same element, leading to unreliable results. This can be due to dynamic elements or changes to the page structure, making it difficult to select elements using standard selectors.

Solution:

To resolve this issue, it’s recommended to use data-testid attributes to identify elements. This can be done by adding the following code to the HTML file:

<button data-testid="copy-button">Copy</button>

Then, in the test file, the following code can be used to select the button:

cy.get("[data-testid='copy-button']").click();

This solution ensures that the test is able to consistently select the correct element, even if changes are made to the page structure.

Problem: Interferences from Other Running Applications

A third common issue with Cypress is interference from other running applications. This can occur when tests are running in the background, causing unpredictable behavior and leading to failed tests. This can be especially problematic when using third-party plugins or libraries that are not optimized for use with Cypress.

Solution:

To resolve this issue, it’s recommended to isolate the test environment as much as possible. This can be done by disabling any unnecessary applications and services and by running tests in a clean environment, such as a virtual machine. Additionally, it’s recommended to use the cy.tick() method to force a wait between actions, to give the system time to process and complete any ongoing operations. The following code demonstrates this solution:

cy.tick(1000);
cy.get("[data-testid='copy-button']").click();
cy.tick(1000);

Here, the cy.tick() method is used to wait for 1 second between each action, allowing the system time to process and complete any ongoing operations, reducing the chances of interference from other running applications.

Problem: Handling Network Latency and Timeouts

When testing applications that make API calls, network latency can cause unpredictable test results. By default, Cypress waits for a request to complete in 4 seconds. This might not always be enough time for your API requests to complete, leading to test failures.

Solution:

To handle this issue, Cypress provides a command called cy.wait() that allows you to wait for a specified amount of time or for a condition to be met. The solution to this problem is to use cy.wait() in your tests to give your API requests enough time to complete.

cy.wait(2000) // waits for 2 seconds

cy.request('https://jsonplaceholder.typicode.com/posts')
  .its('status')
  .should('eq', 200)
  .then(response => {
    cy.wait(response.body.length * 100) // waits for N * 100 milliseconds, where N is the number of posts
  })

Another solution is to increase the default timeout by using the timeout property in cypress.json or in the cypress.config.js file.

// cypress.json
{
  "defaultCommandTimeout": 10000
}

// or in cypress.config.js
module.exports = {
  defaultCommandTimeout: 10000
}

It is important to note that increasing the default timeout too much might lead to slow and unreliable tests, so it is important to find a balance between waiting for API requests to complete and avoiding slow test runs.

A brief introduction to Cypress

Cypress is a modern end-to-end testing framework that enables developers to write, run and debug tests for web applications. It is designed to work on both the front-end and back-end of web applications, allowing developers to test everything from the UI to the API. Cypress operates on the same runtime as the application being tested, which eliminates the traditional challenges associated with testing web applications like slow response times and flaky tests.

Cypress utilizes a powerful API that allows developers to interact with their web application in real-time. With Cypress, developers can perform actions like clicking buttons, filling out forms, and navigating between pages to test the application’s behavior. The API is designed to be simple and intuitive, making it easy for developers to get started with testing. Cypress also provides a comprehensive suite of assertion libraries that allow developers to write tests that verify the behavior of their web application. Additionally, Cypress provides a real-time reloading feature that makes it easy for developers to see their changes reflected in their tests in real-time, streamlining the testing process and reducing the time spent debugging tests.

Most popular use cases for Cypress

  1. End-to-end testing of web applications: Cypress can be used to test the entire flow of a web application, from the user interface to the database. This enables developers to catch bugs and fix them before users do. The following code block demonstrates how to test the functionality of a login form in Cypress:
describe('Login Form', () => {
  it('allows a user to login', () => {
    cy.visit('/login')
    cy.get('input[name="email"]').type('user@example.com')
    cy.get('input[name="password"]').type('password')
    cy.get('button[type="submit"]').click()
    cy.contains('Welcome, user@example.com')
  })
})
  1. Automation of manual testing: Cypress can automate repetitive manual testing processes, freeing up time and resources for other important tasks. For example, it can be used to automate the process of checking if all the links on a web page are functional, as shown in the following code block:
describe('Links', () => {
  it('are functional', () => {
    cy.visit('/')
    cy.get('a').each(($a) => {
      cy.wrap($a).click()
      cy.url().should('not.contain', '404')
    })
  })
})
  1. Real-time reloading: Cypress provides real-time reloading, which means that any changes made to the code are immediately reflected in the test results. This enables developers to quickly see the impact of their changes and make adjustments accordingly. Additionally, Cypress provides detailed debugging information and an interactive test runner, making it easier to find and fix issues.
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.