Web-first assertions and getByTestId against this hub. Serve it on localhost:8000 first.
npm init playwright@latest
# choose TypeScript or JavaScript, adds tests/ + playwright.config
npx playwright test # run
npx playwright test --ui # watch modepip install pytest-playwright
playwright install # download browsers
pytestPlaywright's default test-id attribute is data-testid; this hub uses data-test. Set it once, then use the clean getByTestId() API.
// playwright.config.js
module.exports = {
use: {
baseURL: 'http://localhost:8000',
testIdAttribute: 'data-test', // <- point at our attribute
},
};# conftest.py
import pytest
from playwright.sync_api import expect
@pytest.fixture(autouse=True)
def _testid():
# applies to expect + page.get_by_test_id
from playwright.sync_api import Locator # noqa
import playwright.sync_api as pw
pw.Playwright.selectors = getattr(pw.Playwright, 'selectors', None)
# simplest: just use css selectors [data-test='...'] in Pythonconst { test, expect } = require('@playwright/test');
test('login and reach inventory', async ({ page }) => {
await page.goto('/swaglabs/index.html');
await page.getByTestId('username').fill('standard_user');
await page.getByTestId('password').fill('Pass@123');
await page.getByTestId('login-button').click();
// web-first assertion auto-waits for the grid
await expect(page.locator("[data-test^='item-']").first()).toBeVisible();
// add the first product, open cart, checkout
await page.locator("[data-test^='add-']").first().click();
await expect(page.getByTestId('cart-badge')).toHaveText('1');
await page.getByTestId('cart-icon').click();
await page.getByTestId('checkout').click();
await page.getByTestId('first-name').fill('Ava');
await page.getByTestId('last-name').fill('Tester');
await page.getByTestId('postal-code').fill('12345');
await page.getByTestId('continue').click();
await page.getByTestId('finish').click();
await expect(page.getByTestId('complete-header')).toContainText('Thank you');
});from playwright.sync_api import Page, expect
def test_login_checkout(page: Page):
page.goto('http://localhost:8000/swaglabs/index.html')
page.locator("[data-test='username']").fill('standard_user')
page.locator("[data-test='password']").fill('Pass@123')
page.locator("[data-test='login-button']").click()
expect(page.locator("[data-test^='item-']").first).to_be_visible()
page.locator("[data-test^='add-']").first.click()
expect(page.locator("[data-test='cart-badge']")).to_have_text('1')The Testbed → #dynamic-loading. No explicit wait needed — the assertion retries until it passes.
await page.goto('/internet/index.html#dynamic-loading');
await page.getByTestId('start').click();
await expect(page.getByTestId('finish')).toBeVisible(); // retries automaticallypage.on('dialog', dialog => {
console.log(dialog.type(), dialog.message());
dialog.accept(); // or dialog.dismiss(); prompt: dialog.accept('text')
});
await page.getByTestId('js-confirm').click();Demo Elements → #frames. Use a frame locator — no manual context switching.
await page.goto('/elements/index.html#frames');
const frame = page.frameLocator("[data-test='frame1']");
await expect(frame.locator("[data-test='frame-heading']")).toBeVisible();await page.goto('/elements/index.html#droppable');
await page.getByTestId('drag-me').dragTo(page.getByTestId('drop-here'));
await expect(page.getByTestId('output')).toContainText('Dropped');sleep patterns you fight in Selenium mostly disappear. The hub's random-delay challenges are a good place to feel that.| getByTestId('username') | CartPilot login username |
| [data-test^='add-'] | CartPilot add-to-cart buttons |
| getByTestId('cart-badge') | Cart count badge |
| getByTestId('finish') | Checkout finish / dynamic-loading result |
| getByTestId('js-confirm') | Confirm dialog trigger |
| frameLocator([data-test='frame1']) | Demo Elements iframe |
| getByTestId('drag-me' / 'drop-here') | Demo Elements droppable |