Aprender / Cheat sheet
Cheat sheet de Playwright
As APIs do Playwright Test que você usa todos os dias, em uma folha só. Os locators aparecem na ordem que o Playwright recomenda: role, label e texto antes de test ids, e CSS ou XPath por último. Todas as asserções aqui são web-first: elas tentam de novo até passar ou dar timeout, então você não precisa de esperas manuais.
Conferido com Playwright Test 1.5x, TypeScript
Executar e depurar
| Tarefa | Código |
|---|---|
| Novo projeto | npm init playwright@latest |
| Rodar todos os testes | npx playwright test |
| Um arquivo, uma linha | npx playwright test tests/login.spec.ts:12 |
| Por título ou tag | npx playwright test --grep @smoke |
| Um único projeto de navegador | npx playwright test --project=chromium |
| Ver o navegador | npx playwright test --headed |
| Executar passo a passo com o inspector | npx playwright test --debug |
| UI mode (watch, time travel) | npx playwright test --ui |
| Só as falhas da última execução | npx playwright test --last-failed |
| Caçar um teste flaky | npx playwright test my.spec.ts --repeat-each=20 |
| Dividir em shards entre máquinas de CI | npx playwright test --shard=1/4 |
| Abrir o relatório HTML | npx playwright show-report |
| Abrir um trace | npx playwright show-trace trace.zip |
| Gravar um script | npx playwright codegen https://example.com |
Locators (preferidos primeiro)
| Tarefa | Código |
|---|---|
| Por role e nome acessível | page.getByRole('button', { name: 'Sign in' }) |
| Campo de formulário pelo label | page.getByLabel('Email') |
| Por placeholder | page.getByPlaceholder('Search') |
| Por texto (exato) | page.getByText('Welcome back', { exact: true }) |
| Por test id (data-testid) | page.getByTestId('checkout-submit') |
| CSS | page.locator('#orders tbody tr') |
| XPath | page.locator('xpath=//table[@id="orders"]//tr') |
| Filtrar por texto | page.getByRole('row').filter({ hasText: 'Mei Lin' }) |
| Filtrar por filho | page.getByRole('listitem').filter({ has: page.getByRole('button', { name: 'Buy' }) }) |
| Encadear dentro de um resultado | row.getByRole('button', { name: 'Cancel' }) |
| Escolher um entre vários | .first() .last() .nth(2) |
| Dentro de um iframe | page.frameLocator('#payment').getByLabel('Card number') |
Locators são lazy: nada é buscado até você agir ou fazer uma asserção, então defina-os uma vez e reutilize.
Ações
| Tarefa | Código |
|---|---|
| Navegar (relativo à baseURL) | await page.goto('/login') |
| Clicar | await locator.click() |
| Substituir o valor de um input | await locator.fill('mei@example.com') |
| Digitar tecla por tecla | await locator.pressSequentially('abc', { delay: 50 }) |
| Pressionar uma tecla | await locator.press('Enter') |
| Checkbox ou radio | await locator.check() / await locator.uncheck() |
| Selecionar uma opção | await page.getByLabel('Country').selectOption('UK') |
| Fazer upload de um arquivo | await locator.setInputFiles('fixtures/invoice.pdf') |
| Passar o mouse (hover) | await locator.hover() |
| Arrastar e soltar | await source.dragTo(target) |
| Aceitar o próximo diálogo | page.once('dialog', (d) => d.accept()) |
Web-first assertions
| Tarefa | Código |
|---|---|
| Visível / oculto | await expect(locator).toBeVisible() / .toBeHidden() |
| Texto exato | await expect(locator).toHaveText('Order placed') |
| Texto contém | await expect(locator).toContainText('placed') |
| Valor do input | await expect(input).toHaveValue('42') |
| Número de resultados | await expect(page.getByRole('row')).toHaveCount(5) |
| Habilitado / desabilitado / marcado | toBeEnabled() toBeDisabled() toBeChecked() |
| Atributo | await expect(menu).toHaveAttribute('aria-expanded', 'true') |
| URL e título | await expect(page).toHaveURL(/dashboard/) / .toHaveTitle('Orders') |
| Negar | await expect(spinner).not.toBeVisible() |
| Continuar depois de uma falha | await expect.soft(total).toHaveText('£40.00') |
| Repetir qualquer verificação assíncrona | await expect.poll(async () => (await api.get(url)).status()).toBe(200) |
| Comparação visual | await expect(page).toHaveScreenshot('home.png') |
Rede e API
| Tarefa | Código |
|---|---|
| Mockar uma resposta | await page.route('**/api/orders', (r) => r.fulfill({ json: [] })) |
| Fazer uma requisição falhar | await page.route('**/api/orders', (r) => r.abort()) |
| Alterar uma resposta real | await page.route('**/api/me', async (r) => { const res = await r.fetch(); const json = await res.json(); await r.fulfill({ response: res, json: { ...json, plan: 'pro' } }); }) |
| Esperar por uma resposta | const res = page.waitForResponse('**/api/orders');
await page.getByRole('button', { name: 'Save' }).click();
expect((await res).ok()).toBeTruthy(); |
| Teste de API | test('list users', async ({ request }) => {
const res = await request.get('/api/users');
await expect(res).toBeOK();
expect(await res.json()).toHaveLength(3);
}); |
Estrutura, fixtures e hooks
| Tarefa | Código |
|---|---|
| Agrupar testes | test.describe('checkout', () => { /* tests */ }) |
| Executar antes de cada teste | test.beforeEach(async ({ page }) => { await page.goto('/'); }) |
| Adicionar tag a um teste | test('pays by card', { tag: '@smoke' }, async ({ page }) => { /* ... */ }) |
| Pular condicionalmente | test.skip(browserName === 'webkit', 'Not supported on WebKit') |
| Paralelismo dentro de um arquivo | test.describe.configure({ mode: 'parallel' }) |
| Fixture customizada | export const test = base.extend<{ loginPage: LoginPage }>({
loginPage: async ({ page }, use) => { await use(new LoginPage(page)); },
}); |
| Reutilizar um estado logado | test.use({ storageState: 'playwright/.auth/user.json' }) |
| Nomear um passo no relatório | await test.step('add to cart', async () => { /* ... */ }) |
playwright.config.ts
| Configuração | Exemplo |
|---|---|
| URL base para page.goto | use: { baseURL: 'http://localhost:3000' } |
| Evidências em caso de falha | use: { trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure' } |
| Retries só na CI | retries: process.env.CI ? 2 : 0 |
| Paralelismo | fullyParallel: true, workers: process.env.CI ? 2 : undefined |
| Falhar se um test.only for commitado | forbidOnly: !!process.env.CI |
| Subir a aplicação antes | webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI } |
| Navegadores | projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }] |
| Timeouts padrão | timeout: 30_000, expect: { timeout: 5_000 } |