Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Short answer: you cannot switch Cypress’s normal command runner to another browser tab or window. Cypress is designed to drive one tab. Test a target="_blank" link by asserting its destination (and, if useful, checking it with cy.request()); use cy.origin() only when navigation stays in the same tab but crosses origins; use the documented @cypress/puppeteer integration when you truly must automate a second tab.

First decide what “new tab” means in your test

Teams often call three different behaviors “switching tabs.” They need different Cypress solutions:

What the application does What the test must prove Use
A link has target="_blank" or opens a window The destination URL is correct Assert the link’s href
The destination should respond The URL is reachable over HTTP cy.request(); this does not render or control a new tab
The current tab navigates to another origin Commands must run on that origin cy.origin()
A second tab must be clicked, read, or otherwise controlled Real multi-tab interaction The documented @cypress/puppeteer integration, with setup matched to your Cypress and plugin versions

This distinction prevents the most common mistake: trying to use cy.origin() as a tab switcher. Cypress’s API documentation explicitly lists different tabs and windows among the scenarios that cy.origin() cannot handle.

Verify a link that opens a new tab

Assert the destination without clicking

If the requirement is “the report link points to the right place,” inspect the anchor. This keeps the test in Cypress’s supported single-tab model and makes the assertion precise.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
describe('external report link', () => {
  it('points to the expected report', () => {
    cy.visit('/account');

    cy.get('[data-cy="report-link"]')
      .should('have.attr', 'target', '_blank')
      .and('have.attr', 'href', 'https://reports.example.com/monthly');
  });
});

Use the selector and URL format your application actually emits. Some applications render an absolute URL, while others use a root-relative path such as /reports/monthly. Assert the form that is part of your contract rather than normalizing it accidentally.

Check that the destination responds with cy.request()

When availability matters in addition to the link value, make an HTTP request to the destination:

it('links to a reachable report endpoint', () => {
  cy.visit('/account');

  cy.get('[data-cy="report-link"]')
    .should('have.attr', 'href')
    .then((href) => {
      cy.request(href).its('status').should('be.within', 200, 399);
    });
});

cy.request() verifies an HTTP response. It does not prove that a browser rendered the page, that client-side JavaScript ran, that authentication in a separate tab succeeded, or that the new tab behaved correctly. Keep the assertion and the request as separate checks so a passing request is not mistaken for a browser interaction test.

When a link is generated after an action

Trigger the action in the current tab, then inspect the resulting anchor or URL. Do not claim that Cypress has taken control of a second window merely because the application created one.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
it('creates the export link after requesting an export', () => {
  cy.visit('/exports');
  cy.get('[data-cy="create-export"]').click();

  cy.get('[data-cy="download-export"]')
    .should('have.attr', 'href')
    .and('match', /^https://downloads.example.com/exports//);
});

Use cy.origin() for a different origin in the same tab

A different origin is not the same problem as a different tab. If a click or form submission navigates the existing tab from your application to another origin, put commands for the destination inside cy.origin(url, callbackFn).

it('completes the hosted sign-in flow in the current tab', () => {
  cy.visit('https://app.example.com/start');
  cy.get('[data-cy="sign-in"]').click();

  cy.origin('https://login.example.net', () => {
    cy.get('input[name="username"]').type(Cypress.env('LOGIN_USER'));
    cy.get('input[name="password"]').type(Cypress.env('LOGIN_PASSWORD'), {
      log: false
    });
    cy.get('button[type="submit"]').click();
  });

  cy.url().should('include', '/dashboard');
});

The callback is where Cypress commands execute against the destination origin. The application still has one active browser tab; cy.origin() supplies the cross-origin context needed by Cypress’s same-origin security model.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Cypress 14 and the broader origin rule

Starting with Cypress v14.0.0, tests require cy.origin() when moving between any two different origins, even when the hosts share a superdomain. Check your installed Cypress version before diagnosing a suddenly failing cross-origin test.

The injectDocumentDomain setting can temporarily restore older behavior, but Cypress documents it as deprecated and warns that it can cause problems for sites using origin-keyed agent clusters. Treat it as a short-lived compatibility measure, not a way to automate another tab.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What cy.origin() cannot do

  • It cannot select a second tab by index.
  • It cannot return control to a different window handle.
  • It cannot make Cypress commands run in a tab that the browser opened separately.

If the browser remains on one tab and only the origin changes, use cy.origin(). If a second tab must be operated, use a multi-tab integration instead.

Control a genuine second tab with the Puppeteer integration

Cypress’s migration guidance identifies @cypress/puppeteer as the route for workflows that truly need another tab or window. This is an additional integration with the underlying browser, not a built-in Cypress command. The Playwright-to-Cypress migration guidance describes configuring the integration and handling a browser message to switch tabs and retrieve content.

When the extra setup is justified

  • A payment, identity, or consent flow deliberately opens a second tab.
  • The requirement includes interacting with controls that exist only in that tab.
  • Verifying an href or making an HTTP request would leave a critical user behavior untested.

Before adding it, confirm that a destination assertion is not enough. A plugin introduces browser-level setup, another compatibility surface, and maintenance whenever Cypress or the integration changes.

Plan the integration around its versioned API

Install the package and follow the current Cypress migration instructions for your project’s versions:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm install --save-dev @cypress/puppeteer

The integration’s configuration and browser-message handlers are version-sensitive. Keep that setup in Cypress’s Node-side configuration, not in a normal test command, and use the integration’s documented message to identify the newly opened page and retrieve its content. Do not present a plugin callback as if it were cy.switchTab(); Cypress itself still has no such command.

A robust multi-tab test should also define how it knows the correct page: match the expected URL or another stable page property, wait for the page to finish the required navigation, perform the needed interaction, and return an explicit result to the Cypress test. Avoid relying on “the second page” or an incidental opening order when several tabs can exist.

A practical decision procedure

  1. Name the behavior. Is it a new tab, a new window, or navigation in the current tab?
  2. Reduce the assertion. If the business rule is the destination, assert href. If it is reachability, add cy.request().
  3. Check the origin. For one-tab navigation to another origin, wrap destination commands in cy.origin().
  4. Escalate only for interaction. Use @cypress/puppeteer when a second tab itself must be controlled.
  5. Record the boundary. Document whether the test proves a URL, an HTTP response, a rendered page, or a real multi-tab interaction.

Common failures and fixes

“I used cy.origin(), but the popup is still inaccessible.”

Cause: the application opened another tab or window. cy.origin() addresses origin changes in the current tab only.

Fix: test the link destination, or move to the documented Puppeteer integration if the second tab must be automated.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“The href assertion passes, but the report is unusable.”

Cause: an href proves the link value, not that the destination responds or renders correctly.

Fix: add a focused cy.request() status check for availability. For rendered behavior, create a browser test in a supported one-tab flow or use the multi-tab integration.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

“cy.request() succeeds, but the new tab shows an error.”

Cause: HTTP reachability and browser rendering are different checks. Authentication, JavaScript, redirects, CSP, and browser-only behavior may change the result.

Fix: keep the request as an API-level check and separately test the browser behavior that matters.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“A formerly working cross-origin test fails after upgrading Cypress.”

Cause: Cypress v14.0.0 requires cy.origin() between different origins more broadly than earlier releases.

Fix: identify every origin transition and wrap commands for the destination in the matching cy.origin() block. Review, rather than permanently depending on, the deprecated injectDocumentDomain workaround.

“The Puppeteer integration works locally but fails in CI.”

Cause: browser launch flags, installed browser versions, environment variables, or plugin/Cypress version differences can change browser-level behavior.

Fix: pin compatible versions, use the same browser family locally and in CI, log the page URL selected by the integration, and wait for a deterministic URL or readiness condition instead of a fixed timing guess.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to capture a page for a visual artifact rather than interact with a second tab, ScreenshotNeo can return a screenshot or PDF from one API request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the parameter reference in the ScreenshotNeo documentation. The same endpoint supports full-page and element captures, device and viewport settings, dark mode, retina scale, PDF options, custom CSS or JavaScript, clicks, selector waits, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes 1,000 shots a month free with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan. Create a free ScreenshotNeo account to try the API.

FAQ

Should a test require the target="_blank" attribute?

Only if opening a separate browsing context is itself a product requirement. If the requirement is simply the destination, assert the URL and avoid coupling the test to an implementation detail that may later change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Is a second tab the same as a second origin?

No. A tab is a browser context; an origin is a security boundary made from scheme, host, and port. One tab can navigate across origins, while two tabs can even display the same origin.

What should a test name when it fails?

State the exact contract: wrong destination, unreachable endpoint, cross-origin command failure, or second-tab interaction failure. That wording points directly to the appropriate fix instead of suggesting that every problem needs a tab switch.

Frequently Asked Questions

Should a test require the target=”_blank” attribute?

Only if opening a separate browsing context is itself a product requirement. If the requirement is simply the destination, assert the URL and avoid coupling the test to an implementation detail that may later change.

Is a second tab the same as a second origin?

No. A tab is a browser context; an origin is a security boundary made from scheme, host, and port. One tab can navigate across origins, while two tabs can even display the same origin.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should a test name when it fails?

State the exact contract: wrong destination, unreachable endpoint, cross-origin command failure, or second-tab interaction failure. That wording points directly to the appropriate fix instead of suggesting that every problem needs a tab switch.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.