Playwright JavaScript Framework Best Practices

Iniciado por joomlamz, Hoje at 18:25

Respostas: 1   |   Visualizações: 3

Tópico anterior - Tópico seguinte

0 Membros e 2 Visitantes estão a ver este tópico.

Saudações, caros colegas do **webmastersmz.com**! Como especialista em tecnologia, estive a analisar o tópico **"Playwright JavaScript Framework Best Practices"** e trago aqui um resumo técnico dos pontos mais relevantes para elevarmos a qualidade dos nossos testes end-to-end (E2E).

O Playwright tem-se consolidado como uma das ferramentas mais potentes para automação de navegadores modernas, superando muitas das limitações dos seus antecessores. Para extrairmos o máximo rendimento deste *framework*, o tópico destaca algumas práticas recomendadas fundamentais:

1. **Utilização de Locators Resilientes:** O documento enfatiza o uso de seletores baseados na experiência do utilizador (como `getByRole`, `getByText` e `getByLabel`) em vez de seletores CSS frágeis ou IDs arbitrários. Isto reduz drasticamente a quebra de testes quando a estrutura do DOM muda.
2. **Gestão de Estados e Autenticação (Setup Global):** Uma prática excelente abordada é a reutilização do estado de autenticação. Em vez de fazermos login em cada teste (o que consome muito tempo), podemos configurar um script de *global setup* que guarda os cookies/tokens e os injeta diretamente nos testes seguintes.
3. **Isolamento e Paralelização:** O Playwright executa testes em contextos de navegador isolados por predefinição. O tópico reforça a importância de tirar partido da execução paralela nativa para acelerar os pipelines de CI/CD sem comprometer a integridade dos dados.
4. **Tratamento de Assincronicidade (Auto-waiting):** Devemos confiar no mecanismo nativo de *auto-waiting* do Playwright. Adicionar *hardcoded sleeps* (`setTimeout`) é um anti-patrón que torna os testes instáveis (*flaky*).

Estas práticas não só garantem uma cobertura de testes mais robusta, como também otimizam os recursos dos nossos servidores durante a integração contínua.

Agora, a palavra passa para vocês, comunidade! Como é que têm lidado com a instabilidade (*flakiness*) nos vossos testes E2E? Já adotaram o Playwright nos vossos projetos ou ainda continuam a usar Cypress ou Selenium? Deixem as vossas opiniões e experiências aqui nos comentários para enriquecermos este debate!

---

Para garantir que os vossos projetos e fóruns rodam sem falhas e com a máxima velocidade, convido-vos a conhecer as soluções de alojamento de alta performance da AplicHost em [https://aplichost.com](https://aplichost.com).

Playwright JavaScript Framework Best Practices



Tópico: Playwright JavaScript Framework Best Practices
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------
Playwright JavaScript Framework — Best Practices

A comprehensive guide for writing reliable, maintainable, and scalable end-to-end tests using Playwright with JavaScript (and Cucumber BDD) in the KeyControl test automation framework.



Table of Contents


• Project Structure & Organization

• Page Object Model (POM)

• Selectors & Locators

• Assertions

• Waiting Strategies

• Test Isolation & State Management

• Authentication & Login

• Test Data Management

• BDD / Cucumber Integration

• Error Handling & Debugging

• Retries & Flakiness

• Parallelism & Performance

• Configuration Management

• Reporting & Observability

• CI/CD Integration

• Security & Secrets

• Code Quality & Maintainability

• Accessibility & Cross-Browser Testing



1. Project Structure & Organization




DO


• Keep a flat, predictable folder structure that mirrors the application's domain (e.g., admin/, performer/, approver/, ess/).

• Co-locate feature files, step definitions, and page objects by module/domain so related code is easy to find.

• Use index.js barrel exports to avoid long relative import paths.

• Store all environment-specific configuration in a single config.js at the root; never hardcode URLs or credentials inside test files.



DON'T


• Don't scatter page objects and step definitions randomly across the project.

• Don't mix UI concerns with business logic in the same file.

Recommended Layout

features/

kc/

Admin_Group_Management_Module.feature

Performer_Task_Management_KC1.feature

step-definitions/

kc/

KeyControlAdminSteps.js

KeyControlPerformerSteps.js

page-objects/

kc/

basepage/

KeyControlAdminPage.js

KeyControlPerformerPage.js

utils/

logger.js

ExcelHelper.js

setup/

hooks.js

assertions.js

config.js


Page Object Model (POM)

DO

• Encapsulate all page interactions (clicks, fills, navigations) inside dedicated Page Object classes.

• Keep page objects thin — they should only expose methods, not assertions.

• Compose complex pages from smaller component objects (e.g., TableComponent, ModalComponent).

• Accept the Playwright page instance via the constructor; never create a new browser context inside a POM.
// Good — page-objects/kc/keycontrol/KeyControlAdminPage.js
export class KeyControlAdminPage {
constructor(page) {
this.page = page;

// Pre-define locators for reuse

this.groupNameInput = page.locator('[data-ga="group-name"]');

this.saveButton = page.locator('[data-ga="save-button"]');

this.successBanner = page.locator('[data-ga="success-banner"]');

}

async navigateToGroupManagement() {

await this.page.click('[data-ga="group-management"]');

}

async createGroup(groupData) {

await this.groupNameInput.fill(groupData.name);

await this.saveButton.click();

}

}

DON'T

• Don't put expect() assertions inside page objects — keep them in step definitions or test files.

• Don't duplicate selectors across multiple files; define them once in the page object.

• Selectors & Locators
Priority Order (most preferred → least preferred)
| Priority | Strategy | Example |
|---|---|---|
| 1 | data-ga / data-test attributes | [data-ga="save-button"] |
| 2 | ARIA roles & labels | page.getByRole('button', { name: 'Save' }) |
| 3 | Playwright built-in locators | page.getByLabel('Username') |
| 4 | CSS class (stable, non-generated) | .kc-modal-title |
| 5 | XPath | //div[@class="header"] |
DO

• Use data-ga or data-test attributes — they are immune to styling and structural changes.

• Use Playwright's semantic locators (getByRole, getByLabel, getByText, getByPlaceholder) for readability and resilience.

• Define locators as class properties in page objects to avoid string duplication.

• Use chaining to scope locators: page.locator('.modal').locator('[data-ga="confirm"]').
// Semantic locator
await page.getByRole('button', { name: 'Submit' }).click();

// data-ga attribute

await page.locator('[data-ga="group-name"]').fill('Automation Group');

DON'T

• Don't use auto-generated class names (div.sc-abc123) or positional XPaths (/div[3]/span[1]).

• Don't use page.$() (legacy Playwright API) — always use page.locator().

• Assertions
DO

• Always use Playwright's built-in expect — it has automatic retry, built-in timeouts, and clear error messages.

• Prefer web-first assertions that wait for the UI state to match:
// Web-first assertions (auto-retry)
await expect(page.locator('[data-ga="success-banner"]')).toBeVisible();
await expect(page.locator('[data-ga="user-count"]')).toHaveText('5');
await expect(page.locator('[data-ga="save-button"]')).toBeEnabled();

// Use soft assertions when you want to collect multiple failures in one test run:

const softExpect = expect.configure({ soft: true });

await softExpect(heading).toHaveText('Dashboard');

await softExpect(logo).toBeVisible();

// All soft assertion failures are reported at the end

DON'T

• Don't use page.isVisible() in if statements as a substitute for assertions.

• Don't hard-code waitForTimeout before an assertion — let expect do the waiting.

• Waiting Strategies
DO

• Rely on Playwright's auto-waiting — most click, fill, and expect operations auto-wait for elements to be actionable.

• Use waitForSelector or waitForResponse only for specific async operations not covered by auto-waiting.

• Wait for network responses when actions trigger API calls:
// Wait for API response after action
const [response] = await Promise.all([
page.waitForResponse(resp => resp.url().includes('/api/groups') && resp.status() === 200),
page.locator('[data-ga="save-button"]').click()
]);

// Use page.waitForLoadState('networkidle') only for pages with complex background requests.

DON'T

• Never use arbitrary page.waitForTimeout(3000) — this is a top cause of slow, flaky tests.

• Don't poll visibility in a loop; use expect(...).toBeVisible({ timeout: 10000 }) instead.

• Test Isolation & State Management
DO

• Each Cucumber scenario must be fully independent — it should not rely on state left by a previous scenario.

• Use Before / After hooks in setup/hooks.js to:

• Create a fresh browser context per scenario.

• Navigate to a known starting page.

• Clean up created test data after each scenario.
// setup/hooks.js
Before(async function () {
this.context = await browser.newContext();
this.page = await this.context.newPage();
});

After(async function (scenario) {

if (scenario.result.status === 'FAILED') {

await this.page.screenshot({ path: reports/screenshots/${scenario.pickle.name}.png });

}

await this.context.close();

});

// Use tagged hooks to apply setup only to relevant scenarios:

Before({ tags: '@admin' }, async function () {

await loginAsAdmin(this.page);

});

DON'T

• Don't share page instances or logged-in sessions across unrelated scenarios.

• Don't depend on execution order — scenarios should be runnable in any order.

• Authentication & Login
DO

• Reuse authenticated state using Playwright's storageState to avoid repeating login for every scenario:
// Save auth state once
await page.context().storageState({ path: 'setup/auth-state.json' });

// Reuse in playwright.config.js

use: {

storageState: 'setup/auth-state.json'

}

// Store credentials only in environment variables — never in code or feature files.

// Use a dedicated loginAsRole utility function to support multiple user roles cleanly.

// utils/auth.js

export async function loginAs(page, role) {

const creds = {

admin: { user: process.env.ADMIN_USER, pass: process.env.ADMIN_PASS },

performer: { user: process.env.PERFORMER_USER, pass: process.env.PERFORMER_PASS },

approver: { user: process.env.APPROVER_USER, pass: process.env.APPROVER_PASS },

ess: { user: process.env.ESS_USER, pass: process.env.ESS_PASS }

};

await page.goto(process.env.KEYCONTROL_URL);

await page.fill('[data-ga="username"]', creds[role].user);

await page.fill('[data-ga="password"]', creds[role].pass);

await page.click('[data-ga="login-button"]');

await expect(page.locator('[data-ga="dashboard"]')).toBeVisible();

}

Microsoft SSO

• See MICROSOFT_SSO_TESTING.md for handling Azure AD / Microsoft login flows.

• Mock or bypass SSO in lower environments whenever possible to speed up test execution.

• Test Data Management
DO

• Keep test data separate from test logic — store in test-data/json/ or test-data/excel/.

• Use unique data per run (e.g., timestamps, UUIDs) to prevent collisions when tests run in parallel.
const groupName = AutoGroup_${Date.now()};

// Use factory functions to generate test data objects:

// utils/dataFactory.js

export function createGroupPayload(overrides = {}) {

return {

name: AutoGroup_${Date.now()},

description: 'Generated by automation',

type: 'standard',

...overrides

};

}

// Clean up all data created during a test in the After hook.

DON'T

• Don't hardcode test data (names, IDs, dates) inside step definitions.

• Don't leave orphaned test data in shared environments — it causes noise for manual testers.

• BDD / Cucumber Integration
Feature File Best Practices

• Write scenarios from the user's perspective using Given / When / Then.

• One scenario = one behavior. Don't write "super scenarios" that test 10 things at once.

• Use Background for common pre-conditions, not complex setup logic.

• Use tags consistently to allow selective execution:
@smoke @keycontrol @admin @group-management
Feature: Admin Group Management Module

Background:

Given I am logged in as an admin

@create-group

Scenario: Admin creates a new group

When I navigate to Group Management

And I create a group with name "AutoGroup"

Then the group "AutoGroup" should appear in the list

Step Definition Best Practices

• Keep steps atomic and reusable across scenarios.

• Use World object (this) to share state between steps within a scenario — never use module-level globals.

• Avoid logic-heavy step definitions; delegate to page objects.

• Thin step, rich page object:

When('I create a group with name {string}', async function (name) {

await this.adminPage.createGroup(name);

});


Use Cucumber Data Tables and Doc Strings for structured input data.

• Error Handling & Debugging
DO

• Enable screenshots on failure in After hooks (see Section 6).

• Enable video recording for CI runs to replay failures:

// playwright.config.js

use: {

video: 'retain-on-failure',

screenshot: 'only-on-failure',

trace: 'retain-on-failure'

}

• Use the Playwright Trace Viewer (npx playwright show-trace trace.zip) to inspect failing steps.

• Use the built-in logger (utils/logger.js) instead of console.log for structured output.


Add meaningful step names so traces and reports are human-readable.

Debugging Locally



Run in headed mode with Playwright Inspector


PWDEBUG=1 npx playwright test



Run a single scenario by tag


npx test --tags="@create-group"



Slow down execution for visual debugging


npx playwright test --headed --slowmo=500

• Retries & Flakiness
Retry Policy

• Set retries: 1 (max 2) in playwright.config.js for CI — enough to handle transient network issues, not enough to hide real bugs.

• Never increase retries as a fix for a broken test — find and fix the root cause.
// playwright.config.js
retries: process.env.CI ? 1 : 0,

Flakiness Thresholds

| Level | Threshold | Action |

|---|---|---|

| Acceptable | < 1% over 7 days | Monitor |

| Warning | 1% – 5% | Investigate & RCA |

| Critical | > 5% | Block release, escalate |

Common Flakiness Causes & Fixes

| Cause | Fix |

|---|---|

| waitForTimeout | Replace with expect or waitForResponse |

| Fragile selectors | Switch to data-ga / ARIA locators |

| Shared state | Isolate each scenario (see Section 6) |

| Race conditions | Use Promise.all + network wait |

| Dynamic content | Use toBeVisible({ timeout }) instead of hard-wait |

• Parallelism & Performance
DO

• Start with workers: 2 in CI and increase only after flakiness stabilizes below 1%.

• Use Playwright sharding to distribute tests across CI runners:
npx playwright test --shard=1/3
npx playwright test --shard=2/3
npx playwright test --shard=3/3

• Group slow scenarios (e.g., approval workflows) with @slow tags and run them separately.

• Use storageState to avoid redundant logins (see Section 7).
DON'T

• Don't share a single browser context between parallel workers.

• Don't run all scenarios in parallel before verifying they are properly isolated.

• Configuration Management
DO

• Use a single config.js as the source of truth for all configuration values.

• Override config values via environment variables — never change the config file between environments.

• Use .env files for local development; inject secrets via CI environment variables in pipelines.

// config.js

export const config = {

baseUrl: process.env.KEYCONTROL_URL || 'https://qa-keycontrol.axaxl-cloud.com',

browser: process.env.BROWSER || 'edge',

headless: process.env.HEADLESS !== 'false',

defaultTimeout: Number(process.env.DEFAULT_TIMEOUT) || 120000,

actionTimeout: Number(process.env.ACTION_TIMEOUT) || 30000,

};

• See ENV_SETUP.md for full environment variable documentation.

DON'T

• Don't commit .env files with real credentials to source control.


Don't hardcode environment URLs in test files — always reference config.js.

• Reporting & Observability
DO

• Use Allure as the primary report for rich test history, attachments, and trend analysis.

• Attach screenshots, videos, and traces to Allure on failure automatically via hooks.

• Add Allure labels to enrich reports with metadata:

// in step definitions

this.allure.attachment('Response Body', JSON.stringify(responseBody, null, 2), 'application/json');

• Generate reports as part of every CI pipeline run:

npx run generate:report # Generate Allure report

npx run open:report     # Open in browser locally


Maintain a weekly flakiness dashboard from Allure history data and share with the team.

Report Locations

| Type | Location |

|---|---|

| Allure HTML | allure-report/ |

| Allure Results | allure-results/ |

| JSON Report | reports/cucumber_report.json |

| Screenshots | reports/screenshots/ |

| Videos | test-results/ |

• CI/CD Integration
DO

• Run tests in headless mode in CI:

npm run test:ci

• Fail the pipeline on any test failure — don't silently ignore failed tests.

• Archive Allure results and test-results as CI artifacts for post-run analysis.

• Run linting (eslint) and type checks before executing tests in the pipeline.


Use branch-specific tags to run only smoke tests on PRs and full regression on main:



Example GitHub Actions step


• name: Run Smoke Tests (PR)
run: npm test -- --tags "@smoke"
if: github.event_name == 'pull_request'

• name: Run Full Suite (Main)
run: npm test
if: github.ref == 'refs/heads/main'

DON'T

• Don't run tests against production environments from CI without explicit approval gates.

• Don't skip report generation in CI — reports are essential for debugging failures.

• Security & Secrets
DO

• Store all credentials in environment variables or a secrets manager (Azure Key Vault, GitHub Secrets).

• Reference secrets via process.env.VAR_NAME — never hardcode them.

• Use .gitignore to exclude .env, auth-state.json, and any file that may contain tokens.

• Rotate test user passwords regularly and update secrets in the CI store.

• See SECRETS.md for detailed secrets management guidelines.
DON'T

• Don't log credentials or tokens to the console or report output.

• Don't commit auth-state.json (contains session tokens) to source control.

• Don't use personal accounts as test users — use dedicated service accounts.

• Code Quality & Maintainability
DO

• Enable ESLint with a consistent style guide (e.g., Airbnb or StandardJS).

• Use ES Modules (import/export) consistently — the project already uses "type": "module" in package.json.

• Use async/await everywhere — avoid mixing Promises and callbacks.

• Keep functions small and single-purpose (\le 20 lines is a good target).

• Write JSDoc comments for all page object methods and utilities.

• Review test code in pull requests — test code is production code.
/**

• Creates a new group with the given data.


@param {Object} groupData


@param {string} groupData.name


@param {string} [groupData.description]
*/
async createGroup(groupData) {
await this.groupNameInput.fill(groupData.name);
await this.saveButton.click();
await expect(this.successBanner).toBeVisible();
}

DON'T

• Don't leave commented-out code or console.log statements in committed code.

• Don't duplicate step definitions — extract shared steps into a common-steps.js.

• Don't use var — always use const or let.

• Accessibility & Cross-Browser Testing
Accessibility

• Prefer ARIA roles and labels as primary selectors — this naturally validates accessibility.

• Run @axe-core/playwright checks on key pages as part of accessibility regression:
import { checkA11y } from 'axe-playwright';

await checkA11y(page, undefined, {

runOnly: ['wcag2a', 'wcag2aa']

});

Cross-Browser

• The framework targets Microsoft Edge (Chromium) as the primary browser.

• Add firefox and webkit project entries in playwright.config.js for critical smoke tests when cross-browser coverage is required:

projects: [

{ name: 'edge', use: { ...devices['Desktop Edge'], channel: 'msedge' } },

{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },

{ name: 'webkit', use: { ...devices['Desktop Safari'] } }

]

• Run cross-browser tests on a schedule (e.g., nightly), not on every PR, to keep PR pipelines fast.

Quick Reference Checklist

Use this checklist before merging new tests:

• Tests are isolated — no shared state between scenarios

• data-ga or ARIA locators used — no fragile XPath/CSS

• No waitForTimeout calls

• Assertions use Playwright's expect (web-first)

• Credentials are in environment variables, not hardcoded

• Test data uses unique identifiers (timestamp/UUID)

• Screenshots/video capture is configured on failure

• Feature file has correct tags for selective execution

• Page objects contain no assertions

• Code reviewed and ESLint passes

References

• Playwright Official Documentation

• Playwright Best Practices

• Cucumber.js Documentation

• Allure Playwright Integration

• ENV_SETUP.md

SELECTORS.md

FLAKINESS.md

SECRETS.md

• MICROSOFT_SSO_TESTING.md

• CI_HARNESS.md


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: