Módulo 3: Cenários do site de treino
Login compartilhado entre testes, loading assíncrono, paginação, estado vazio, modal e upload de arquivo.
🎥 Prefere ver antes de ler?
Esse vídeo (4min08) explica o regex do toHaveURL, a query string que força a
lista vazia, a diferença entre toContainText e toHaveText, como o
upload de arquivo funciona sem abrir a janela do sistema, e termina com tudo isso acontecendo
de verdade no site de treino, incluindo o arrastar e soltar da galeria. Não substitui o texto
abaixo, mas ajuda a visualizar antes de praticar.
1. Reaproveitando a LoginPage do Módulo 1
/index.html, /produto.html e
/carrinho.html exigem sessão: sem login, redirecionam pra
/login.html. O Módulo 2 te ensinou Page Object Model; aqui está o primeiro
pagamento desse investimento: em vez de reescrever o preenchimento do login, criamos
pages/LoginPage.js (mesma ideia da CadastroPage) e
reaproveitamos ela, o mesmo fluxo que o Módulo 1 já testou:
const { LoginPage } = require("../pages/LoginPage");
async function login(page) {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login("maria@exemplo.com", "SenhaForte123");
await expect(page).toHaveURL(/index\.html/);
}
test.describe("lista de produtos", () => {
test.beforeEach(async ({ page }) => {
await login(page);
});
// ...
});
O await expect(page).toHaveURL(...) no fim da
função importa: o clique dispara um fetch com atraso proposital antes de
redirecionar. Sem esperar a URL mudar, o próximo passo do teste pode rodar contra a
tela de login ainda meio carregada.
2. Loading assíncrono, paginação e estado vazio
A lista de produtos também ganhou uma Page Object
(pages/ProdutosPage.js). Ela busca os dados com um atraso proposital: dá
pra testar o carregamento de verdade, não um "pisca" rápido demais pra reparar:
const produtos = new ProdutosPage(page);
await expect(produtos.produto(1)).toBeVisible();
await expect(produtos.loadingSpinner).toBeHidden();
A mesma página tem um parâmetro pra forçar lista vazia, sem precisar mexer no
servidor, útil pra testar o estado vazio isoladamente. É por isso que
ProdutosPage.goto() aceita uma opção:
await produtos.goto({ vazio: true }); // navega pra /index.html?vazio=1
await expect(produtos.emptyState).toBeVisible();
E a paginação é só clicar e conferir o número atual:
await produtos.paginationNext.click();
await expect(produtos.paginationCurrent).toHaveText("2");
3. Modal de confirmação
Ao adicionar um produto ao carrinho, um modal confirma. A
ProdutoPage (pages/ProdutoPage.js) já espera o detalhe do
produto carregar dentro do próprio goto(id). O botão "adicionar" não faz
nada se o produto ainda não carregou (não há indicador disso na tela, vale anotar como
melhoria de UX pra reportar), então esperar faz parte de "abrir a página" nessa classe,
não algo que cada teste precisa lembrar de repetir:
const produto = new ProdutoPage(page);
await produto.goto(1); // ja espera o titulo carregar
await produto.addToCartButton.click();
await expect(produto.modalMessage).toContainText("foi adicionado");
4. Upload de arquivo
setInputFiles simula a escolha de um arquivo sem
precisar abrir o seletor nativo do sistema operacional:
await produto.fileInput.setInputFiles(
path.join(__dirname, "..", "fixtures", "exemplo.png")
);
await expect(produto.filePreview).toContainText("exemplo.png");
Funciona mesmo com o accept="image/*" do campo:
esse atributo só filtra o que aparece no seletor manual, não bloqueia
setInputFiles.
5. Além da trilha: fixtures (test.extend)
O padrão que você acabou de praticar — criar
new LoginPage(page) dentro de um beforeEach — funciona e é
assim que esta trilha ensina do início ao fim. Mas em projetos Playwright de médio/grande
porte, o jeito recomendado pela documentação oficial é empacotar isso numa
fixture customizada, com test.extend. A ideia: em vez de repetir
"crie o Page Object e faça login" em cada arquivo de teste, você declara isso uma vez e
o Playwright injeta pronto, exatamente como já injeta { page }:
// fixtures.js
const base = require("@playwright/test");
const { LoginPage } = require("./pages/LoginPage");
exports.test = base.test.extend({
paginaLogada: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login("maria@exemplo.com", "SenhaForte123");
await base.expect(page).toHaveURL(/index\.html/);
await use(page); // o teste roda aqui, ja logado
},
});
exports.expect = base.expect;
No teste, troca-se o require("@playwright/test") pelo arquivo de fixtures,
e o beforeEach de login some: quem pede paginaLogada no teste
já recebe a página logada, sem escrever a lógica de novo:
const { test, expect } = require("../fixtures");
test("produtos aparecem depois do carregamento", async ({ paginaLogada }) => {
await expect(paginaLogada.getByTestId("product-list")).toBeVisible();
});
Por que isso importa: o beforeEach manual
que esta trilha usa não escala bem quando você tem várias combinações de setup (logado
como admin, logado sem produtos no carrinho, logado com sessão expirada...) — vira um
emaranhado de funções auxiliares. Fixtures resolvem isso com composição: cada teste pede
só as fixtures que precisa, e o Playwright cuida da ordem e da limpeza. Esta trilha fica
no beforeEach de propósito, por ser mais fácil de enxergar o que acontece
quando você está aprendendo — mas se o próximo projeto usar test.extend,
agora você já reconhece o padrão.
6. Os testes deste módulo
Arquivo já existe em
tests/03_cenarios.spec.js. Não precisa digitar, só rodar:
npx playwright test tests/03_cenarios.spec.js
Exercício
A galeria do produto tem drag and drop de verdade (eventos HTML5
nativos, não um plugin). Em tests/03_cenarios.spec.js, dentro do
describe da página de produto, escreva um teste chamado
"arrastar o primeiro item da galeria pro final reordena a lista" usando
produto.galleryItem(n) e .dragTo():
await produto.galleryItem(1).dragTo(produto.galleryItem(3));
Depois confira a nova ordem com
produto.ordemDaGaleria() (já existe na ProdutoPage) e
confirme que o item 1 não é mais o primeiro. Rode python verificar.py 3 até
dar 7 [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
│ ├── CadastroPage.js
│ ├── ProdutosPage.js
│ ├── ProdutoPage.js ← Page Object usado neste exercício
│ └── RedePage.js
└── tests/
├── 01_primeiro_teste.spec.js
├── 02_selecionadores.spec.js
├── 03_cenarios.spec.js ← este exercício
└── 04_rede_ci.spec.js
Olhar de QA
Repare que cada teste deste módulo espera por algo
específico antes de agir (o título do produto não estar vazio, o spinner sumir).
Nenhum usa sleep(). Isso não é estilo, é necessidade: o atraso artificial
das APIs varia, e esperar "um tempo fixo" cedo demais produz teste instável (passa na
sua máquina, falha na do colega ou no CI, que costuma ser mais lento). Esperar a
condição certa em vez do tempo certo é o que separa teste de UI confiável
de teste de UI que ninguém confia mais depois de algumas semanas.