Módulo 2: Page Object Model, formulário e validação
Agrupar testes com test.describe, organizar o projeto com Page Object Model, e conferir mensagem de validação de campo.
🎥 Prefere ver antes de ler?
Esse vídeo (2min52) explica por que organizar com Page Object Model, mostra a classe
CadastroPage por dentro, o que é um constructor, como
module.exports/require conectam os arquivos, e termina com uma
validação de senha curta acontecendo de verdade no site de treino. Não substitui o texto
abaixo, mas ajuda a visualizar antes de praticar.
1. Agrupar testes com describe e beforeEach
Quando vários testes compartilham a mesma página ou o mesmo
preparo, agrupe com test.describe e repita a parte comum num
test.beforeEach, que roda antes de cada teste do grupo, com uma
página nova e limpa:
test.describe("cadastro", () => {
test("campos obrigatorios mostram erro ao submeter em branco", async ({ page }) => {
await page.goto("/cadastro.html");
// ...
});
test("cadastro valido mostra mensagem de sucesso", async ({ page }) => {
await page.goto("/cadastro.html");
// ...
});
});
Repare: cada teste recebe um page novo, porque o
Playwright isola testes entre si (contexto de navegador zerado). Por isso um teste nunca
vê o estado que o anterior deixou. É o que torna a ordem de execução irrelevante.
2. Por que organizar com Page Object Model
O Módulo 1 escreveu page.getByTestId("login-email-input")
direto no teste. Funciona bem pra um arquivo com dois testes, mas pensa em escala: a
trilha inteira (e um projeto de automação de verdade) acaba com dezenas de
testes tocando as mesmas páginas. Se o data-testid de um campo mudar, você
teria que caçar e trocar em todo teste que usa aquela página.
Page Object Model (POM) resolve isso: cada página do site vira uma
classe, que guarda os locators e as ações daquela página. O teste chama métodos
com nome de negócio (cadastro.preencher(...)), não detalhes de seletor:
// pages/CadastroPage.js
class CadastroPage {
constructor(page) {
this.page = page;
this.nameInput = page.getByTestId("signup-name-input");
this.emailInput = page.getByTestId("signup-email-input");
this.passwordInput = page.getByTestId("signup-password-input");
this.submitButton = page.getByTestId("signup-submit-button");
this.successMessage = page.getByTestId("signup-success-message");
this.nameError = page.getByTestId("signup-field-error-name");
this.emailError = page.getByTestId("signup-field-error-email");
this.passwordError = page.getByTestId("signup-field-error-password");
}
async goto() {
await this.page.goto("/cadastro.html");
}
async preencher({ nome = "", email = "", senha = "" }) {
await this.nameInput.fill(nome);
await this.emailInput.fill(email);
await this.passwordInput.fill(senha);
}
async submeter() {
await this.submitButton.click();
}
}
module.exports = { CadastroPage };
constructor(page) é um método especial de toda classe: ele roda sozinho,
uma vez, no exato momento em que alguém escreve new CadastroPage(page) (isso
acontece no beforeEach do teste, mais abaixo). É o lugar certo pra preparar
tudo que o objeto vai precisar. Aqui ele recebe o page do Playwright (o
mesmo que o Módulo 1 usava direto no teste) e guarda, em this.algumaCoisa,
um locator pronto pra cada campo e botão da tela de cadastro. Esses this.algumaCoisa
ficam acessíveis pros outros métodos da classe (preencher, submeter)
e pro teste, como cadastro.passwordError faz lá na frente.
Arquivo já existe em pages/CadastroPage.js. Se o
data-testid de um campo mudar amanhã, você corrige em um lugar, a
classe, e todo teste que usa CadastroPage continua funcionando.
3. Usando a Page Object no teste
O teste cria a página no beforeEach e usa seus
métodos e locators. Repare como fica mais curto e mais fácil de ler, quase uma frase:
const { CadastroPage } = require("../pages/CadastroPage");
test.describe("cadastro", () => {
let cadastro;
test.beforeEach(async ({ page }) => {
cadastro = new CadastroPage(page);
await cadastro.goto();
});
test("cadastro valido mostra mensagem de sucesso", async () => {
await cadastro.preencher({ nome: "Joana", email: "joana@exemplo.com", senha: "SenhaForte123" });
await cadastro.submeter();
await expect(cadastro.successMessage).toBeVisible();
});
});
Note que o teste não chama page.getByTestId nem
uma vez. Isso é o que fica "escondido" dentro da Page Object: o teste descreve
comportamento (preencher, submeter, ver sucesso); a classe sabe
como fazer isso nesta página específica.
4. toBeVisible e toBeHidden
Validação de formulário é o lugar clássico pra testar os dois
lados de uma asserção: o erro certo aparece, e os errados não aparecem.
toBeHidden() é o oposto de toBeVisible(): também espera
sozinho, só que até o elemento sumir (ou não existir, ou estar com
display:none):
await expect(cadastro.passwordError).toBeVisible();
await expect(cadastro.nameError).toBeHidden();
Testar só o "caminho feliz" (tudo certo) ou só o erro mais óbvio deixa passar bug de validação cruzada: por exemplo, um campo que acende o erro errado, ou todos os erros acendendo juntos quando só um campo estava errado.
5. Os testes deste módulo
Arquivo já existe em
tests/02_selecionadores.spec.js. Não precisa digitar, só rodar (com o
site de treino ligado):
npx playwright test tests/02_selecionadores.spec.js
Bug real encontrado nesta trilha: o formulário de cadastro tinha
required nos campos, e a validação nativa do navegador bloqueava o
envio antes do JavaScript customizado rodar, então as mensagens de erro por campo nunca
apareciam de verdade. A correção foi adicionar novalidate no
<form>, pra deixar a validação customizada (a que tem
data-testid e o teste consegue verificar) ser a única a rodar. Se você
testou um formulário e a asserção nunca falha nem passa do jeito esperado, vale suspeitar
disso: o navegador pode estar resolvendo antes do seu código.
Exercício
Em tests/02_selecionadores.spec.js, dentro do
mesmo describe("cadastro", ...), escreva um teste chamado
"senha curta mostra so o erro de senha" usando a cadastro
(a CadastroPage já criada no beforeEach): preenche nome e
e-mail válidos, senha com menos de 6 caracteres, submete, e confere que só o erro
de senha aparece (os de nome e e-mail continuam escondidos). Rode
npx playwright test tests/02_selecionadores.spec.js e depois
python verificar.py 2 até dar 4 [ok]. Gabarito em
trilha-7/GABARITO.md.
📁 Ver estrutura de pastas da trilha-7/
trilha-7/
├── playwright.config.js
├── package.json
├── verificar.py
├── GABARITO.md
├── README.md
├── fixtures/
│ └── exemplo.png
├── pages/
│ ├── LoginPage.js ← Page Object, já existia
│ ├── CadastroPage.js ← Page Object, usado neste exercício
│ ├── ProdutosPage.js
│ ├── ProdutoPage.js
│ └── RedePage.js
└── tests/
├── 01_primeiro_teste.spec.js
├── 02_selecionadores.spec.js ← este exercício
├── 03_cenarios.spec.js
└── 04_rede_ci.spec.js
Olhar de QA
Formulário de validação é um dos lugares onde teste automatizado paga mais rápido: são várias combinações de campo válido/inválido, fáceis de esquecer num teste manual repetitivo, e cada uma é uma linha de teste aqui. O cuidado é não parar no "apareceu uma mensagem de erro": confira qual mensagem, e que as outras não apareceram. Teste que só confere "tem erro na tela" aceita um sistema que mostra a mensagem errada pro campo certo.
E sobre organização: Page Object Model não é burocracia, é o que separa um projeto de automação que qualquer um do time consegue manter de um que só quem escreveu entende. Nos próximos módulos, repare como o Módulo 3 reaproveita o login que o Módulo 1 já testou, em vez de reescrever o fluxo de novo. É o POM pagando o investimento.