VALIDATION LABNEW TAB / WINDOW

Navigation

New Tab / Window

Click a link that opens a new tab and assert against the content of the popup page.

Advanced

Scenario

Magic links, third-party OAuth, pricing pages — every product opens new tabs. Tests that ignore them lose coverage on the entire post-link experience. `context.waitForEvent('page')` is the canonical pattern.

Open the destination in a new tab; assert against popup.

Open destination ↗

Live widget · interact freely

Manual test checklist

  • 1Click — a new tab opens with the destination
  • 2Confirm `target='_blank'` AND `rel='noreferrer noopener'` (security)
  • 3Close the popup, return to the original tab — original session intact
  • 4Open via Cmd-click — same destination
  • 5Confirm the destination page itself is linkable (has its own URL)

Expected result

The popup loads /practice/new-tab/destination and exposes a recognizable heading.

Automation challenge

Set up `const popupPromise = context.waitForEvent('page')` BEFORE the click. Await it after, then call `popup.waitForLoadState('domcontentloaded')` and assert against `popup.getByRole(...)`.

Stable selectors

  • External link[data-testid="tab-link"]

Locator strategy

Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.

Use simple IDs and names to understand how locating elements works.

1
2
3
await page.goto('https://lab.hakdogan.com/practice/new-tab');

await page.locator('#tab-link').click();

Avoid as primary strategies

XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.

1
2
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');

Reference Playwright spec

new-tab.spec.ts
ts
1
2
3
4
5
6
7
8
9
10
11
12
import { test, expect } from '@playwright/test';

test('open external link in new tab', async ({ page, context }) => {
  await page.goto('https://lab.hakdogan.com/practice/new-tab');

  const popupPromise = context.waitForEvent('page');
  await page.getByTestId('tab-link').click();
  const popup = await popupPromise;

  await popup.waitForLoadState('domcontentloaded');
  await expect(popup.getByRole('heading', { name: /destination/i })).toBeVisible();
});

Headed Test Playback

Simulated headed-browser flow — no real browser is launched.

Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.

Idlenew-tab.spec.ts
Failure demos
https://lab.hakdogan.com/practice/new-tab
playwright · headed · chromium0.00s
# awaiting Run Test · terminal scrolls automatically
Steps
  1. Clicking external link
  2. Awaiting popup
  3. Asserting destination heading
StatusIdle
BrowserChromium
FrameworkPlaywright + TypeScript
Elapsed (live)--
Specnew-tab.spec.ts