PracticeQAAutomation logoPracticeQAAutomation
Pw

TypeScript Β· @playwright/test

Element handling with Playwright

23 copy-ready Playwright recipes in TypeScript, JavaScript, Python, Java and C# (.NET), each linked to a live challenge page to run them against.

Language
Conventions Β· TypeScript
// Every snippet runs inside: test('...', async ({ page }) => { await page.goto('/<challenge>'); ... })

1Type into inputs & read output

Practice on /elements/text-box β†’
TypeScript
await page.getByTestId('full-name-input').fill('Ada Lovelace');
await page.getByTestId('submit-text-box').click();
await expect(page.getByTestId('output-name')).toHaveText('Name: Ada Lovelace');
TypeScript
await page.getByTestId('checkbox-select-all').check();
await expect(page.getByTestId('checkbox-documents')).toBeChecked();
TypeScript
await page.getByTestId('native-select').selectOption('Mumbai');
await expect(page.getByTestId('native-select-result')).toHaveText('Selected: Mumbai');
TypeScript
await page.getByTestId('autocomplete-input').fill('Ben');
await page.getByTestId('autocomplete-option-bengaluru').click();
await expect(page.getByTestId('autocomplete-result')).toHaveText('Picked: Bengaluru');

5Find a table row by its text

Practice on /elements/web-tables β†’
TypeScript
const row = page.getByTestId('table-row').filter({ hasText: 'Priya' });
await row.locator('[data-testid^="delete-row-"]').click();
await expect(page.getByTestId('table-row')).toHaveCount(2);

6Double-click & right-click

Practice on /elements/buttons β†’
TypeScript
await page.getByTestId('double-click-me').dblclick();
await page.getByTestId('right-click-me').click({ button: 'right' });
await expect(page.getByTestId('right-click-result')).toBeVisible();

7JS alert, confirm & prompt

Practice on /alerts β†’
TypeScript
page.once('dialog', (d) => d.accept('Ada'));   // register BEFORE the click
await page.getByTestId('prompt-button').click();
await expect(page.getByTestId('prompt-result')).toHaveText('You entered Ada');

page.once('dialog', (d) => d.dismiss());
await page.getByTestId('confirm-button').click();
await expect(page.getByTestId('confirm-result')).toHaveText('You selected Cancel');

8Iframes & nested iframes

Practice on /frames β†’
TypeScript
const frame = page.frameLocator('[data-testid="single-frame"]');
await frame.getByTestId('single-frame-button').click();
await expect(frame.getByTestId('single-frame-result')).toHaveText('Clicked inside iframe');

const inner = page.frameLocator('[data-testid="outer-frame"]')
  .frameLocator('[data-testid="innermost-frame"]');
await expect(inner.getByTestId('innermost-text')).toBeVisible();

9New tabs & windows

Practice on /windows β†’
TypeScript
const [tab] = await Promise.all([
  page.context().waitForEvent('page'),
  page.getByTestId('new-tab-button').click(),
]);
await expect(tab.getByTestId('new-tab-heading')).toBeVisible();
TypeScript
await page.getByTestId('draggable-box').dragTo(page.getByTestId('accept-zone'));
await expect(page.getByTestId('accept-zone')).toContainText('Accept (1)');
TypeScript
await page.getByTestId('file-input').setInputFiles('fixtures/sample.txt');
await expect(page.getByTestId('uploaded-file-info')).toContainText('sample.txt');
TypeScript
await page.getByTestId('tooltip-trigger').hover();
await expect(page.getByTestId('tooltip')).toBeVisible();

13Wait for dynamically loaded content

Practice on /dynamic/dynamic-loading β†’
TypeScript
await page.getByTestId('loading-1-start').click();
// Auto-waits up to the expect timeout (5s by default).
await expect(page.getByTestId('loading-1-result')).toContainText('Hello World!');
TypeScript
// CSS locators pierce open shadow roots automatically.
await page.locator('shadow-counter #inc').click();
await expect(page.locator('shadow-counter #count')).toHaveText('Count: 1');

15Nested & closed shadow roots

Practice on /shadow-dom β†’
TypeScript
// Nested open roots: getByTestId pierces every level.
await page.getByTestId('nested-input').fill('Ada');
await page.getByTestId('nested-submit').click();
await expect(page.getByTestId('nested-result')).toHaveText('Hello, Ada!');

// Closed root: nothing can query inside β€” drive it with the keyboard.
await page.getByTestId('closed-host').click();          // delegatesFocus β†’ the input
await page.keyboard.type('Ada');
await page.keyboard.press('Tab');                        // β†’ Submit button
await page.keyboard.press('Enter');
await expect(page.getByTestId('closed-host')).toHaveAttribute('data-result', 'Hello, Ada!');

16Shadow DOM inside an iframe

Practice on /shadow-dom β†’
TypeScript
const frame = page.frameLocator('[data-testid=shadow-in-iframe]');
await frame.getByTestId('frame-nested-input').fill('Ada');
await frame.getByTestId('frame-nested-submit').click();
await expect(frame.getByTestId('frame-nested-result')).toHaveText('Hello, Ada!');

17Cross-origin iframe

Practice on /frames β†’
TypeScript
// Playwright handles cross-origin frames like any other.
const frame = page.frameLocator('[data-testid=cross-origin-frame]');
await frame.getByTestId('xorigin-input').fill('Ada');
await frame.getByTestId('xorigin-submit').click();
await expect(frame.getByTestId('xorigin-result')).toHaveText('Hello, Ada!');

18Element covered by an overlay

Practice on /elements/hidden-elements β†’
TypeScript
// A plain click waits, then fails: "<div …> intercepts pointer events".
await page.getByTestId('remove-overlay').click();
await page.getByTestId('covered-button').click();
await expect(page.getByTestId('covered-result')).toBeVisible();
// Last resort only: click({ force: true }) skips actionability checks.

19Tick a table row by another cell's text

Practice on /elements/web-tables β†’
TypeScript
await page.getByRole('row', { name: /Farah Siddiqui/ }).getByRole('checkbox').check();
await expect(page.getByTestId('selected-users')).toContainText('EMP-1043');

20Verify a column sorts correctly (numbers, not text)

Practice on /elements/web-tables β†’
TypeScript
await page.getByTestId('sort-amount').click();
await expect(page.getByTestId('sort-th-amount')).toHaveAttribute('aria-sort', 'ascending');
// "β‚Ή1,299.00" β†’ 1299 β€” compare as numbers, never as strings
const nums = (await page.locator('[data-testid="sort-row"] [data-col="amount"]').allTextContents())
  .filter((t) => t !== 'β€”').map((t) => Number(t.replace(/[^\d.]/g, '')));
expect(nums).toEqual([...nums].sort((a, b) => a - b));

21normalize-space(), pseudo-elements & DOM comments

Practice on /elements/xpath-functions β†’
TypeScript
// Accessible names are whitespace-normalised, so getByLabel just works.
await page.getByLabel('Delivery Address').fill('12 MG Road');
// …or the XPath way:
await page.locator("xpath=//label[normalize-space()='Delivery Address']/following::input[1]").fill('12 MG Road');

// ::before text isn't in the DOM β€” read the computed style.
const before = await page.getByTestId('pseudo-price').evaluate((el) => getComputedStyle(el, '::before').content);
expect(before).toContain('β‚Ή');

// HTML comments: evaluate an XPath that returns the comment's text.
const code = await page.evaluate(() => document.evaluate(
  "//span[@data-testid='comment-target']/following-sibling::comment()",
  document, null, XPathResult.STRING_TYPE, null).stringValue.trim());
expect(code).toBe('secret-code: QA-2026');
TypeScript
const row = page.locator('[data-order-id="ORD-1042"]');
while (!(await row.isVisible())) {
  await page.getByTestId('orders-next').click();        // throws if we run out of pages
}
await expect(row).toContainText('ORD-1042');
TypeScript
await page.getByTestId('key-input').press('Enter');
await expect(page.getByTestId('key-result')).toHaveText('You entered: Enter');

Practicing another tool?