Pular para o conteúdo principal

Testes Web com Serenity Screenplay e Playwright

Introducao

Playwright e uma biblioteca moderna de automacao de navegadores que fornece capacidades de testes cross-browser com excelente suporte para aplicacoes web modernas. O modulo serenity-screenplay-playwright traz o poder do Playwright para o Screenplay Pattern do Serenity BDD, oferecendo uma alternativa a integracao tradicional com WebDriver.

Playwright oferece varias vantagens sobre o WebDriver:

  • Auto-waiting: Espera automaticamente que os elementos estejam acionaveis antes de executar acoes
  • Cross-browser: Suporte nativo para Chromium, Firefox e WebKit
  • Interceptacao de rede: Suporte integrado para mock e interceptacao de requisicoes de rede
  • Rastreamento: Registra rastreamentos detalhados para depurar falhas de teste
  • Emulacao de dispositivos: Facil emulacao de dispositivos moveis e tablets
  • Arquitetura moderna: Design orientado a eventos com melhor confiabilidade

Primeiros Passos

Dependencia Maven

Adicione a seguinte dependencia ao seu projeto:

<dependency>
<groupId>net.serenity-bdd</groupId>
<artifactId>serenity-screenplay-playwright</artifactId>
<version>${serenity.version}</version>
</dependency>

A Ability BrowseTheWebWithPlaywright

Para usar Playwright com Screenplay, os Actor precisam da Ability BrowseTheWebWithPlaywright:

import net.serenitybdd.screenplay.playwright.abilities.BrowseTheWebWithPlaywright;
import com.microsoft.playwright.*;

Playwright playwright = Playwright.create();
Browser browser = playwright.chromium().launch();

Actor alice = Actor.named("Alice");
alice.can(BrowseTheWebWithPlaywright.using(browser));

Configuracao

Voce pode configurar o comportamento do Playwright usando variaveis de ambiente ou programaticamente:

// Iniciar com opcoes
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions()
.setHeadless(false)
.setSlowMo(100);

Browser browser = playwright.chromium().launch(options);

Abrindo uma URL

Abrindo uma URL diretamente

No Screenplay, voce abre uma nova pagina usando a classe de Interaction Navigate:

alice.attemptsTo(Navigate.to("https://todomvc.com/examples/react/"));

Abrindo a URL base

Se voce configurou uma URL base, pode navegar ate ela:

alice.attemptsTo(Navigate.toTheBaseUrl());

Localizando Elementos em uma Pagina

A Classe Target

A classe Target na integracao Playwright usa o poderoso motor de seletores do Playwright. Diferente do WebDriver, o Playwright fornece multiplas estrategias de seletores integradas.

Target SUBMIT_BUTTON = Target.the("Submit button").locatedBy("#submit-btn");

alice.attemptsTo(Click.on(SUBMIT_BUTTON));

Seletores Playwright

Playwright suporta um rico conjunto de seletores:

// Seletor CSS
Target.the("Login button").locatedBy("#login-btn")

// Seletor de texto
Target.the("Submit button").locatedBy("text=Submit")

// Seletor de role (ARIA)
Target.the("Submit button").locatedBy("role=button[name='Submit']")

// Seletor XPath
Target.the("Email field").locatedBy("xpath=//input[@type='email']")

// Combinando seletores
Target.the("Form submit").locatedBy("form >> button[type='submit']")

Target Dinamicos

Voce pode usar Target parametrizados para localizacao dinamica de elementos:

Target MENU_ITEM = Target.the("{0} menu item").locatedBy("text={0}");

alice.attemptsTo(Click.on(MENU_ITEM.of("Settings")));

Fabricas de Elementos UI

O Serenity Playwright fornece classes de fabrica convenientes para localizar elementos UI comuns usando a poderosa sintaxe de seletores do Playwright.

Button

Localiza botoes usando varias estrategias:

import net.serenitybdd.screenplay.playwright.ui.Button;

// Por texto visivel (case-insensitive, usa seletor de role)
alice.attemptsTo(Click.on(Button.withText("Submit")));

// Por atributo name ou ID
alice.attemptsTo(Click.on(Button.withNameOrId("submit-btn")));

// Por aria-label
alice.attemptsTo(Click.on(Button.withAriaLabel("Close dialog")));

// Contendo texto especifico
alice.attemptsTo(Click.on(Button.containingText("Add to")));

// Localizador customizado
alice.attemptsTo(Click.on(Button.locatedBy("[data-testid='primary-action']")));

InputField

Localiza campos de entrada:

import net.serenitybdd.screenplay.playwright.ui.InputField;

// Por name ou ID
alice.attemptsTo(Enter.theValue("john@example.com").into(InputField.withNameOrId("email")));

// Por texto placeholder
alice.attemptsTo(Enter.theValue("Search...").into(InputField.withPlaceholder("Search products")));

// Por texto de label associado
alice.attemptsTo(Enter.theValue("password123").into(InputField.withLabel("Password")));

// Por aria-label
alice.attemptsTo(Enter.theValue("John").into(InputField.withAriaLabel("First name")));

Localiza elementos anchor:

import net.serenitybdd.screenplay.playwright.ui.Link;

// Por texto exato
alice.attemptsTo(Click.on(Link.withText("Learn more")));

// Contendo texto
alice.attemptsTo(Click.on(Link.containingText("documentation")));

// Por atributo title
alice.attemptsTo(Click.on(Link.withTitle("View user profile")));

Checkbox

Localiza inputs checkbox:

import net.serenitybdd.screenplay.playwright.ui.Checkbox;

// Por texto de label
alice.attemptsTo(Click.on(Checkbox.withLabel("Accept terms")));

// Por name ou ID
alice.attemptsTo(Click.on(Checkbox.withNameOrId("newsletter")));

// Por atributo value
alice.attemptsTo(Click.on(Checkbox.withValue("premium")));

RadioButton

Localiza inputs de botao radio:

import net.serenitybdd.screenplay.playwright.ui.RadioButton;

// Por texto de label
alice.attemptsTo(Click.on(RadioButton.withLabel("Express shipping")));

// Por atributo value
alice.attemptsTo(Click.on(RadioButton.withValue("express")));

Localiza elementos select:

import net.serenitybdd.screenplay.playwright.ui.Dropdown;

// Por texto de label
alice.attemptsTo(
SelectFromOptions.byVisibleText("Canada").from(Dropdown.withLabel("Country"))
);

// Por name ou ID
alice.attemptsTo(
SelectFromOptions.byValue("us").from(Dropdown.withNameOrId("country"))
);

Label

Localiza elementos label:

import net.serenitybdd.screenplay.playwright.ui.Label;

// Por conteudo de texto
String labelText = alice.asksFor(Text.of(Label.withText("Email")));

// Para um ID de campo especifico
Target emailLabel = Label.forFieldId("email-input");

Image

Localiza elementos de imagem:

import net.serenitybdd.screenplay.playwright.ui.Image;

// Por texto alt
alice.attemptsTo(Click.on(Image.withAltText("Product thumbnail")));

// Por URL de source
alice.attemptsTo(Click.on(Image.withSrc("/images/logo.png")));

// Por URL de source parcial
alice.attemptsTo(Click.on(Image.withSrcContaining("product-123")));

Interagindo com Elementos

Interaction Principais

As seguintes classes de Interaction estao disponiveis no pacote net.serenitybdd.screenplay.playwright.interactions:

InteractionPropositoExemplo
ClickClicar em um elementoactor.attemptsTo(Click.on("#button"))
DoubleClickClicar duas vezes em um elementoactor.attemptsTo(DoubleClick.on("#item"))
RightClickClicar com botao direito (menu de contexto)actor.attemptsTo(RightClick.on("#menu"))
EnterDigitar em um campo de entradaactor.attemptsTo(Enter.theValue("text").into("#field"))
ClearLimpar um campo de entradaactor.attemptsTo(Clear.field("#field"))
HoverPassar o mouse sobre um elementoactor.attemptsTo(Hover.over("#menu"))
PressPressionar teclas do tecladoactor.attemptsTo(Press.key("Enter"))
CheckMarcar um checkboxactor.attemptsTo(Check.checkbox("#agree"))
UncheckDesmarcar um checkboxactor.attemptsTo(Uncheck.checkbox("#agree"))
FocusFocar em um elementoactor.attemptsTo(Focus.on("#input"))
NavigateNavegar para uma URLactor.attemptsTo(Navigate.to("https://..."))
UploadFazer upload de um arquivoactor.attemptsTo(Upload.file(path).to("#upload"))

Click

Clicar em um elemento. Playwright espera automaticamente que o elemento esteja acionavel:

alice.attemptsTo(Click.on("#submit-button"));
alice.attemptsTo(Click.on(SUBMIT_BUTTON));

Double-Click

Clicar duas vezes em um elemento:

alice.attemptsTo(DoubleClick.on("#item"));

Right-Click

Clicar com botao direito para abrir menus de contexto:

alice.attemptsTo(RightClick.on("#file-item"));

Enter Text

Digitar valores em campos de entrada:

alice.attemptsTo(Enter.theValue("john@example.com").into("#email"));

Voce tambem pode limpar o campo primeiro:

alice.attemptsTo(
Clear.field("#email"),
Enter.theValue("new-email@example.com").into("#email")
);

Interaction de Teclado

Pressionar teclas do teclado:

// Tecla unica
alice.attemptsTo(Press.key("Enter"));

// Combinacoes de teclas
alice.attemptsTo(Press.key("Control+a"));

// Multiplas teclas
alice.attemptsTo(Press.keys("Tab", "Tab", "Enter"));

Hover

Passar o mouse sobre elementos para acionar estados de hover:

alice.attemptsTo(Hover.over("#dropdown-menu"));

Check e Uncheck

Trabalhar com checkboxes:

alice.attemptsTo(Check.checkbox("#newsletter"));
alice.attemptsTo(Uncheck.checkbox("#marketing-emails"));

Focus

Focar em um elemento:

alice.attemptsTo(Focus.on("#search-input"));

Selecionando de Dropdowns

Selecionar opcoes de menus dropdown:

// Por texto visivel
alice.attemptsTo(SelectFromOptions.byVisibleText("Red").from("#color"));

// Por atributo value
alice.attemptsTo(SelectFromOptions.byValue("red").from("#color"));

// Por indice
alice.attemptsTo(SelectFromOptions.byIndex(2).from("#color"));

// Multiplos valores (para multi-select)
alice.attemptsTo(SelectFromOptions.byValue("red", "blue", "green").from("#colors"));

Deselecionando de Dropdowns

Para dropdowns multi-select, voce pode desselecionar opcoes:

import net.serenitybdd.screenplay.playwright.interactions.DeselectFromOptions;

// Desselecionar por value
alice.attemptsTo(DeselectFromOptions.byValue("red").from("#colors"));

// Desselecionar por texto visivel
alice.attemptsTo(DeselectFromOptions.byVisibleText("Red").from("#colors"));

// Desselecionar por indice
alice.attemptsTo(DeselectFromOptions.byIndex(0).from("#colors"));

// Desselecionar todos
alice.attemptsTo(DeselectFromOptions.all().from("#colors"));

Rolagem

Capacidades abrangentes de rolagem:

import net.serenitybdd.screenplay.playwright.interactions.Scroll;

// Rolar ate um elemento
alice.attemptsTo(Scroll.to("#terms-and-conditions"));

// Rolar com alinhamento
alice.attemptsTo(Scroll.to("#section").andAlignToTop());
alice.attemptsTo(Scroll.to("#section").andAlignToCenter());
alice.attemptsTo(Scroll.to("#section").andAlignToBottom());

// Rolagem no nivel da pagina
alice.attemptsTo(Scroll.toTop());
alice.attemptsTo(Scroll.toBottom());

// Rolar por quantidade especifica (deltaX, deltaY)
alice.attemptsTo(Scroll.by(0, 500));

// Rolar para posicao especifica
alice.attemptsTo(Scroll.toPosition(0, 1000));

Drag and Drop

Arrastar elementos de um local para outro:

import net.serenitybdd.screenplay.playwright.interactions.Drag;

// Drag and drop basico
alice.attemptsTo(Drag.from("#source").to("#target"));

// Sintaxe fluente alternativa
alice.attemptsTo(Drag.the("#draggable").onto("#droppable"));

// Com Target
alice.attemptsTo(Drag.from(SOURCE_ELEMENT).to(TARGET_LOCATION));

Uploads de Arquivo

Fazer upload de arquivos:

Path fileToUpload = Paths.get("path/to/file.pdf");
alice.attemptsTo(Upload.file(fileToUpload).to("#file-input"));

Execucao de JavaScript

Executar JavaScript no contexto da pagina:

alice.attemptsTo(
Evaluate.javascript("window.scrollTo(0, document.body.scrollHeight)")
);

// Com valor de retorno
Object result = alice.asksFor(
Evaluate.javascript("return document.title")
);

Espera

Esperar por elementos ou condicoes:

// Esperar elemento estar visivel
alice.attemptsTo(WaitUntil.the("#loading").isNotVisible());

// Esperar elemento estar oculto
alice.attemptsTo(WaitUntil.the("#spinner").isHidden());

// Esperar com timeout
alice.attemptsTo(
WaitUntil.the("#content").isVisible()
.forNoMoreThan(Duration.ofSeconds(10))
);

Consultando a Pagina Web

Question Incluidas

O Serenity Playwright fornece classes Question para consultar o estado da pagina:

QuestionPropositoExemplo
TextObter texto do elementoactor.asksFor(Text.of("#title"))
ValueObter valor do inputactor.asksFor(Value.of("#email"))
AttributeObter valor do atributoactor.asksFor(Attribute.of("#link").named("href"))
PresenceVerificar se elemento existeactor.asksFor(Presence.of("#modal"))
AbsenceVerificar se elemento esta ausenteactor.asksFor(Absence.of("#error"))
VisibilityVerificar se elemento esta visivelactor.asksFor(Visibility.of("#popup"))
EnabledVerificar se elemento esta habilitadoactor.asksFor(Enabled.of("#submit"))
SelectedStatusVerificar se checkbox esta selecionadoactor.asksFor(SelectedStatus.of("#agree"))
CSSValueObter propriedade CSSactor.asksFor(CSSValue.of("#box").named("color"))
CurrentUrlObter URL atual da paginaactor.asksFor(CurrentUrl.ofThePage())
PageTitleObter titulo da paginaactor.asksFor(PageTitle.ofThePage())

Text

Obter o conteudo de texto de um elemento:

String heading = alice.asksFor(Text.of("#main-heading"));

// Multiplos elementos
List<String> items = alice.asksFor(Text.ofEach(".list-item"));

Presence e Absence

Verificar se elementos existem na pagina:

// Verificar se esta presente
boolean isPresent = alice.asksFor(Presence.of("#modal"));

// Verificar se esta ausente
boolean isAbsent = alice.asksFor(Absence.of("#error-message"));

Atributos

Obter valores de atributos:

String href = alice.asksFor(Attribute.of("#link").named("href"));
String placeholder = alice.asksFor(Attribute.of("#input").named("placeholder"));

Informacoes da Pagina Atual

Consultar informacoes no nivel da pagina:

String url = alice.asksFor(CurrentUrl.ofThePage());
String title = alice.asksFor(PageTitle.ofThePage());

Interceptacao e Mock de Rede

O Playwright fornece poderosas capacidades de interceptacao de rede para testes.

Interceptando Requisicoes

import net.serenitybdd.screenplay.playwright.network.InterceptNetwork;

// Interceptar e completar com resposta mock
alice.attemptsTo(
InterceptNetwork.requestsTo("**/api/users")
.andRespondWith(
new Route.FulfillOptions()
.setStatus(200)
.setBody("{\"users\": []}")
.setContentType("application/json")
)
);

// Interceptar e responder com JSON
alice.attemptsTo(
InterceptNetwork.requestsTo("**/api/user/123")
.andRespondWithJson(200, Map.of(
"id", 123,
"name", "John Doe",
"email", "john@example.com"
))
);

Handlers de Requisicao Customizados

Para mais controle, use handlers customizados:

alice.attemptsTo(
InterceptNetwork.requestsTo("**/api/**")
.andHandle(route -> {
if (route.request().method().equals("DELETE")) {
route.fulfill(new Route.FulfillOptions()
.setStatus(403)
.setBody("{\"error\": \"Forbidden\"}"));
} else {
route.resume();
}
})
);

Abortando Requisicoes

Bloquear requisicoes especificas (util para testar tratamento de erros):

alice.attemptsTo(
InterceptNetwork.requestsTo("**/analytics/**").andAbort()
);

Removendo Rotas

Remover handlers de rotas registrados anteriormente:

import net.serenitybdd.screenplay.playwright.network.RemoveRoutes;

// Remover todos os handlers de rota
alice.attemptsTo(RemoveRoutes.all());

// Remover rotas para padrao especifico
alice.attemptsTo(RemoveRoutes.forUrl("**/api/**"));

Integracao de Testes de API

Novo na versao 5.2.2

A integracao de testes de API foi adicionada no Serenity BDD 5.2.2.

Fazer requisicoes de API que compartilham o contexto de sessao do navegador (cookies, autenticacao). Isso permite Scenario de teste hibridos UI + API onde voce pode configurar dados via API, executar acoes de UI e verificar estado atraves de chamadas de API.

Requisicoes de API Basicas

import net.serenitybdd.screenplay.playwright.interactions.api.APIRequest;
import net.serenitybdd.screenplay.playwright.questions.api.LastAPIResponse;

// Inicializar contexto do navegador primeiro (necessario para requisicoes de API)
alice.attemptsTo(Open.url("about:blank"));

// Requisicao GET
alice.attemptsTo(
APIRequest.get("https://api.example.com/users/1")
);

// Requisicao POST com corpo JSON
alice.attemptsTo(
APIRequest.post("https://api.example.com/users")
.withJsonBody(Map.of(
"name", "John Doe",
"email", "john@example.com"
))
);

// Requisicao PUT
alice.attemptsTo(
APIRequest.put("https://api.example.com/users/1")
.withJsonBody(Map.of("name", "Jane Doe"))
);

// Requisicao PATCH
alice.attemptsTo(
APIRequest.patch("https://api.example.com/users/1")
.withJsonBody(Map.of("status", "active"))
);

// Requisicao DELETE
alice.attemptsTo(
APIRequest.delete("https://api.example.com/users/1")
);

// Requisicao HEAD
alice.attemptsTo(
APIRequest.head("https://api.example.com/users/1")
);

Configuracao de Requisicao

// Adicionar headers customizados
alice.attemptsTo(
APIRequest.get("https://api.example.com/data")
.withHeader("Authorization", "Bearer token123")
.withHeader("X-Custom-Header", "value")
);

// Adicionar parametros de query
alice.attemptsTo(
APIRequest.get("https://api.example.com/search")
.withQueryParam("q", "test")
.withQueryParam("limit", "10")
);

// Definir content type
alice.attemptsTo(
APIRequest.post("https://api.example.com/data")
.withBody("<xml>data</xml>")
.withContentType("application/xml")
);

// Definir timeout
alice.attemptsTo(
APIRequest.get("https://api.example.com/slow")
.withTimeout(30000) // 30 segundos
);

// Falhar em codigos de status nao-2xx
alice.attemptsTo(
APIRequest.get("https://api.example.com/data")
.failOnStatusCode(true)
);

Consultando Respostas

// Obter codigo de status
int status = alice.asksFor(LastAPIResponse.statusCode());

// Verificar se resposta esta OK (2xx)
boolean isOk = alice.asksFor(LastAPIResponse.ok());

// Obter corpo da resposta como string
String body = alice.asksFor(LastAPIResponse.body());

// Parsear resposta JSON como Map
Map<String, Object> json = alice.asksFor(LastAPIResponse.jsonBody());

// Parsear resposta de array JSON como List
List<Map<String, Object>> items = alice.asksFor(LastAPIResponse.jsonBodyAsList());

// Obter headers da resposta
Map<String, String> headers = alice.asksFor(LastAPIResponse.headers());
String contentType = alice.asksFor(LastAPIResponse.header("Content-Type"));

// Obter URL final (apos redirecionamentos)
String url = alice.asksFor(LastAPIResponse.url());

Testes Hibridos UI + API

Requisicoes de API compartilham automaticamente cookies com o contexto do navegador:

@Test
void shouldUseAuthenticatedSession() {
// Login via UI
alice.attemptsTo(
Navigate.to("https://myapp.com/login"),
Enter.theValue("user@example.com").into("#email"),
Enter.theValue("password").into("#password"),
Click.on(Button.withText("Login"))
);

// Chamadas de API agora incluem cookies de autenticacao
alice.attemptsTo(
APIRequest.get("https://myapp.com/api/profile")
);

// Verificar resposta de API autenticada
Map<String, Object> profile = alice.asksFor(LastAPIResponse.jsonBody());
assertThat(profile.get("email")).isEqualTo("user@example.com");
}

Chamadas de API nos Relatorios do Serenity

Requisicoes de API feitas via APIRequest sao automaticamente registradas nos relatorios do Serenity, semelhante ao RestAssured. Os relatorios mostram:

  • Metodo HTTP e URL
  • Headers e corpo da requisicao
  • Codigo de status da resposta
  • Headers e corpo da resposta

Isso fornece visibilidade completa das interacoes de API durante a execucao do teste.

Exemplo Completo

@Test
void shouldCreateAndVerifyUser() {
alice.attemptsTo(Open.url("about:blank"));

// Criar usuario via API
alice.attemptsTo(
APIRequest.post("https://jsonplaceholder.typicode.com/users")
.withJsonBody(Map.of(
"name", "Test User",
"email", "test@example.com",
"username", "testuser"
))
);

// Verificar criacao
assertThat(alice.asksFor(LastAPIResponse.statusCode())).isEqualTo(201);

Map<String, Object> createdUser = alice.asksFor(LastAPIResponse.jsonBody());
assertThat(createdUser.get("name")).isEqualTo("Test User");
assertThat(createdUser.get("id")).isNotNull();

// Buscar o usuario criado
String userId = String.valueOf(((Double) createdUser.get("id")).intValue());
alice.attemptsTo(
APIRequest.get("https://jsonplaceholder.typicode.com/users/" + userId)
);

assertThat(alice.asksFor(LastAPIResponse.statusCode())).isEqualTo(200);
}

Lidando com Downloads

Esperar e lidar com downloads de arquivos:

import net.serenitybdd.screenplay.playwright.interactions.WaitForDownload;
import net.serenitybdd.screenplay.playwright.questions.DownloadedFile;

// Esperar download acionado por clique
alice.attemptsTo(
WaitForDownload.whilePerforming(Click.on("#download-btn"))
);

// Consultar informacoes do download
String filename = alice.asksFor(DownloadedFile.suggestedFilename());
String url = alice.asksFor(DownloadedFile.url());
Path path = alice.asksFor(DownloadedFile.path());

// Verificar falhas
String error = alice.asksFor(DownloadedFile.failure());
if (error == null) {
// Download bem-sucedido
}

// Salvar em local especifico
alice.attemptsTo(
WaitForDownload.whilePerforming(Click.on("#export-btn"))
.andSaveTo(Paths.get("/downloads/report.pdf"))
);

Captura de Mensagens do Console

Capturar e consultar mensagens do console do navegador para depuracao:

import net.serenitybdd.screenplay.playwright.interactions.CaptureConsoleMessages;
import net.serenitybdd.screenplay.playwright.questions.ConsoleMessages;

// Iniciar captura de mensagens do console
alice.attemptsTo(CaptureConsoleMessages.duringTest());

// ... executar acoes que podem logar no console ...

// Consultar mensagens capturadas
List<String> allMessages = alice.asksFor(ConsoleMessages.all());
List<String> errors = alice.asksFor(ConsoleMessages.errors());
List<String> warnings = alice.asksFor(ConsoleMessages.warnings());
List<String> logs = alice.asksFor(ConsoleMessages.logs());

// Filtrar por conteudo
List<String> apiErrors = alice.asksFor(ConsoleMessages.containing("API error"));

// Obter contagens de mensagens
int totalCount = alice.asksFor(ConsoleMessages.count());
int errorCount = alice.asksFor(ConsoleMessages.errorCount());

// Limpar mensagens capturadas entre fases do teste
alice.attemptsTo(CaptureConsoleMessages.clear());

Verificando Erros do Console

Novo na versao 5.2.2

CheckConsole foi adicionado no Serenity BDD 5.2.2.

Use CheckConsole para falhar automaticamente testes quando erros ou avisos de JavaScript ocorrem. Esta e a forma recomendada de garantir que sua aplicacao nao tenha erros de console durante fluxos de usuario:

import net.serenitybdd.screenplay.playwright.interactions.CheckConsole;

// Iniciar captura, depois verificar erros no final do fluxo
alice.attemptsTo(
CaptureConsoleMessages.duringTest(),

// Executar acoes do usuario
Navigate.to("https://myapp.com/checkout"),
Enter.theValue("4111111111111111").into("#card-number"),
Click.on(Button.withText("Pay")),

// Falhar o teste se ocorreram erros de JavaScript
CheckConsole.forErrors()
);

Opcoes do CheckConsole

MetodoDescricao
CheckConsole.forErrors()Falha se erros de console forem encontrados
CheckConsole.forWarnings()Falha se avisos de console forem encontrados
CheckConsole.forErrorsAndWarnings()Falha se erros OU avisos forem encontrados

Modo Somente Relatorio

As vezes voce quer documentar problemas do console sem falhar o teste (por exemplo, para problemas conhecidos ou ao monitorar tendencias de erros):

// Relatar erros no Serenity mas nao falhar o teste
alice.attemptsTo(
CheckConsole.forErrors().andReportOnly()
);

Quando erros sao encontrados, eles sao automaticamente anexados ao relatorio do Serenity como evidencia, independentemente de o teste falhar ou nao.

Relatando Mensagens do Console

Novo na versao 5.2.2

ReportConsoleMessages foi adicionado no Serenity BDD 5.2.2.

Use ReportConsoleMessages para adicionar explicitamente mensagens de console capturadas ao relatorio do Serenity:

import net.serenitybdd.screenplay.playwright.interactions.ReportConsoleMessages;

alice.attemptsTo(
CaptureConsoleMessages.duringTest(),

// ... executar acoes ...

// Relatar erros e avisos no Serenity
ReportConsoleMessages.errorsAndWarnings()
);

Opcoes do ReportConsoleMessages

MetodoDescricao
ReportConsoleMessages.all()Relata todas as mensagens do console
ReportConsoleMessages.errors()Relata apenas erros
ReportConsoleMessages.warnings()Relata apenas avisos
ReportConsoleMessages.errorsAndWarnings()Relata erros e avisos

Exemplo: Verificacao Completa de Erros do Console

Aqui esta um padrao tipico para garantir que nenhum erro de JavaScript ocorra durante um fluxo critico de usuario:

@Test
void checkoutFlowShouldHaveNoJavaScriptErrors() {
alice.attemptsTo(
// Iniciar captura no inicio
CaptureConsoleMessages.duringTest(),

// Completar o fluxo de checkout
Navigate.to("https://myapp.com/cart"),
Click.on(Button.withText("Checkout")),
Enter.theValue("john@example.com").into(InputField.withLabel("Email")),
Enter.theValue("4111111111111111").into(InputField.withLabel("Card Number")),
Click.on(Button.withText("Place Order")),

// Verificar confirmacao do pedido
WaitFor.theElement(".order-confirmation").toBeVisible(),

// Falhar o teste se ocorreram erros de JavaScript durante o fluxo
CheckConsole.forErrors()
);
}

Captura de Requisicoes de Rede

Novo na versao 5.2.2

A captura de requisicoes de rede foi adicionada no Serenity BDD 5.2.2.

Capturar e analisar todas as requisicoes de rede feitas durante os testes. Isso e util para depuracao, verificacao de chamadas de API feitas pelo frontend e deteccao de requisicoes falhas.

import net.serenitybdd.screenplay.playwright.interactions.CaptureNetworkRequests;
import net.serenitybdd.screenplay.playwright.interactions.CaptureNetworkRequests.CapturedRequest;
import net.serenitybdd.screenplay.playwright.questions.NetworkRequests;

// Iniciar captura de requisicoes de rede
alice.attemptsTo(CaptureNetworkRequests.duringTest());

// Executar acoes que disparam requisicoes de rede
alice.attemptsTo(Navigate.to("https://example.com"));

// Consultar todas as requisicoes capturadas
List<CapturedRequest> allRequests = alice.asksFor(NetworkRequests.all());
int requestCount = alice.asksFor(NetworkRequests.count());

// Filtrar por metodo HTTP
List<CapturedRequest> getRequests = alice.asksFor(NetworkRequests.withMethod("GET"));
List<CapturedRequest> postRequests = alice.asksFor(NetworkRequests.withMethod("POST"));

// Filtrar por URL
List<CapturedRequest> apiRequests = alice.asksFor(
NetworkRequests.toUrlContaining("/api/")
);

// Filtrar por padrao glob
List<CapturedRequest> cssRequests = alice.asksFor(
NetworkRequests.matching("**/*.css")
);

// Encontrar requisicoes falhas (4xx, 5xx, ou erros de rede)
List<CapturedRequest> failedRequests = alice.asksFor(NetworkRequests.failed());
int failedCount = alice.asksFor(NetworkRequests.failedCount());

// Encontrar erros de cliente (4xx)
List<CapturedRequest> clientErrors = alice.asksFor(NetworkRequests.clientErrors());

// Encontrar erros de servidor (5xx)
List<CapturedRequest> serverErrors = alice.asksFor(NetworkRequests.serverErrors());

// Limpar requisicoes capturadas entre fases do teste
alice.attemptsTo(CaptureNetworkRequests.clear());

Propriedades do CapturedRequest

Cada CapturedRequest contem:

PropriedadeDescricao
getUrl()A URL da requisicao
getMethod()Metodo HTTP (GET, POST, etc.)
getResourceType()Tipo de recurso (document, xhr, fetch, stylesheet, etc.)
getRequestHeaders()Map de headers da requisicao
getStatus()Codigo de status da resposta (ou null se pendente/falha)
getStatusText()Texto de status da resposta
getFailureText()Razao da falha para erros de rede
isFailed()Verdadeiro se a requisicao falhou (4xx, 5xx, ou erro de rede)
isClientError()Verdadeiro se status 4xx
isServerError()Verdadeiro se status 5xx

Exemplo: Verificando Chamadas de API

@Test
void shouldMakeCorrectApiCalls() {
alice.attemptsTo(
CaptureNetworkRequests.duringTest(),
Navigate.to("https://myapp.com"),
Click.on(Button.withText("Load Data"))
);

// Verificar que a chamada de API esperada foi feita
List<CapturedRequest> apiCalls = alice.asksFor(
NetworkRequests.toUrlContaining("/api/data")
);

assertThat(apiCalls).hasSize(1);
assertThat(apiCalls.get(0).getMethod()).isEqualTo("GET");
assertThat(apiCalls.get(0).getStatus()).isEqualTo(200);
}

Captura de Evidencias de Falha

Novo na versao 5.2.2

A captura automatica de evidencias de falha foi adicionada no Serenity BDD 5.2.2.

Quando um teste falha, o Serenity Playwright captura automaticamente informacoes de diagnostico e as anexa ao relatorio. Isso torna a depuracao de falhas de teste muito mais facil.

Coleta Automatica de Evidencias

Quando a captura de mensagens do console ou requisicoes de rede esta habilitada, as seguintes evidencias sao automaticamente coletadas na falha do teste:

  • Informacoes da Pagina: URL atual e titulo da pagina
  • Erros do Console: Todas as mensagens console.error() e console.warn()
  • Requisicoes de Rede Falhas: Requisicoes que retornaram status 4xx/5xx ou erros de rede

Esta evidencia e anexada ao relatorio do Serenity como "Playwright Failure Evidence".

Habilitando Captura de Evidencias

Simplesmente habilite a captura de console e/ou rede no inicio dos seus testes:

@BeforeEach
void setup() {
alice = Actor.named("Alice")
.whoCan(BrowseTheWebWithPlaywright.usingTheDefaultConfiguration());

// Habilitar captura para evidencias de falha
alice.attemptsTo(
CaptureConsoleMessages.duringTest(),
CaptureNetworkRequests.duringTest()
);
}

Acesso Programatico a Evidencias

Voce tambem pode consultar evidencias programaticamente para asserções ou relatorios customizados:

import net.serenitybdd.screenplay.playwright.evidence.PlaywrightFailureEvidence;

// Obter erros do console
List<String> consoleErrors = PlaywrightFailureEvidence.getConsoleErrors(alice);

// Obter requisicoes de rede falhas
List<String> failedRequests = PlaywrightFailureEvidence.getFailedRequests(alice);

// Usar em asserções
assertThat(consoleErrors).isEmpty();
assertThat(failedRequests).isEmpty();

Exemplo: Detectando Erros de JavaScript

@Test
void pageShouldNotHaveJavaScriptErrors() {
alice.attemptsTo(
CaptureConsoleMessages.duringTest(),
Navigate.to("https://myapp.com"),
Click.on(Button.withText("Submit"))
);

// Verificar que nenhum erro de JavaScript ocorreu
List<String> errors = alice.asksFor(ConsoleMessages.errors());
assertThat(errors)
.describedAs("JavaScript errors on page")
.isEmpty();
}

Exemplo: Detectando Chamadas de API Falhas

@Test
void allApiCallsShouldSucceed() {
alice.attemptsTo(
CaptureNetworkRequests.duringTest(),
Navigate.to("https://myapp.com/dashboard")
);

// Verificar que nenhuma chamada de API falhou
List<CapturedRequest> failedRequests = alice.asksFor(NetworkRequests.failed());
assertThat(failedRequests)
.describedAs("Failed network requests")
.isEmpty();
}

Testes de Acessibilidade

Testar conformidade de acessibilidade usando snapshots ARIA:

import net.serenitybdd.screenplay.playwright.questions.AccessibilitySnapshot;
import com.microsoft.playwright.options.AriaRole;

// Obter snapshot de acessibilidade da pagina inteira
String pageSnapshot = alice.asksFor(AccessibilitySnapshot.ofThePage());

// Obter snapshot de acessibilidade de um elemento especifico
String navSnapshot = alice.asksFor(AccessibilitySnapshot.of("#main-nav"));
String formSnapshot = alice.asksFor(AccessibilitySnapshot.of(LOGIN_FORM));

// Obter todos os elementos com um role ARIA especifico
List<String> buttons = alice.asksFor(AccessibilitySnapshot.allWithRole(AriaRole.BUTTON));
List<String> links = alice.asksFor(AccessibilitySnapshot.allWithRole(AriaRole.LINK));
List<String> headings = alice.asksFor(AccessibilitySnapshot.allWithRole(AriaRole.HEADING));

Testes de Regressao Visual

Comparar screenshots contra imagens de baseline:

import net.serenitybdd.screenplay.playwright.assertions.visual.CompareScreenshot;

// Comparacao de pagina inteira
alice.attemptsTo(
CompareScreenshot.ofPage()
.againstBaseline("homepage-baseline.png")
.withThreshold(0.01) // 1% de diferenca permitida
);

// Comparacao de elemento
alice.attemptsTo(
CompareScreenshot.of("#product-card")
.againstBaseline("product-card-baseline.png")
);

// Com mascara para conteudo dinamico
alice.attemptsTo(
CompareScreenshot.ofPage()
.againstBaseline("dashboard.png")
.withMask("#timestamp", "#user-avatar")
);

Emulacao de Dispositivos

Testar designs responsivos com emulacao de dispositivos:

import net.serenitybdd.screenplay.playwright.interactions.EmulateDevice;

// Emular dispositivo especifico
alice.attemptsTo(EmulateDevice.device("iPhone 14"));
alice.attemptsTo(EmulateDevice.device("Pixel 7"));
alice.attemptsTo(EmulateDevice.device("iPad Pro 11"));

// Viewport customizada
alice.attemptsTo(EmulateDevice.withViewport(375, 812));

// Com fator de escala do dispositivo
alice.attemptsTo(
EmulateDevice.withViewport(375, 812).andDeviceScaleFactor(2)
);

// Com user agent mobile
alice.attemptsTo(
EmulateDevice.withViewport(375, 812).asMobile()
);

Geolocalizacao

Testar recursos baseados em localizacao:

import net.serenitybdd.screenplay.playwright.interactions.SetGeolocation;
import net.serenitybdd.screenplay.playwright.interactions.GrantPermissions;

// Primeiro conceder permissao de geolocalizacao
alice.attemptsTo(GrantPermissions.for_("geolocation"));

// Definir coordenadas especificas
alice.attemptsTo(SetGeolocation.to(51.5074, -0.1278)); // Londres

// Usar localizacoes predefinidas
alice.attemptsTo(SetGeolocation.toNewYork());
alice.attemptsTo(SetGeolocation.toLondon());
alice.attemptsTo(SetGeolocation.toTokyo());
alice.attemptsTo(SetGeolocation.toSanFrancisco());
alice.attemptsTo(SetGeolocation.toSydney());
alice.attemptsTo(SetGeolocation.toParis());

// Com precisao
alice.attemptsTo(
SetGeolocation.to(40.7128, -74.0060).withAccuracy(100)
);

// Limpar geolocalizacao
alice.attemptsTo(SetGeolocation.clear());

Gerenciamento de Permissoes

Conceder ou limpar permissoes do navegador:

import net.serenitybdd.screenplay.playwright.interactions.GrantPermissions;
import net.serenitybdd.screenplay.playwright.interactions.ClearPermissions;

// Conceder permissoes especificas
alice.attemptsTo(GrantPermissions.for_("geolocation"));
alice.attemptsTo(GrantPermissions.for_("notifications", "camera", "microphone"));

// Limpar todas as permissoes
alice.attemptsTo(ClearPermissions.all());

Controle do Relogio

Testar funcionalidade dependente de tempo:

import net.serenitybdd.screenplay.playwright.interactions.ControlClock;
import java.time.Instant;

// Instalar relogio falso
alice.attemptsTo(ControlClock.install());

// Definir para hora especifica
alice.attemptsTo(
ControlClock.setTo(Instant.parse("2024-01-15T10:30:00Z"))
);

// Avancar o tempo
alice.attemptsTo(ControlClock.advanceBy(Duration.ofHours(2)));
alice.attemptsTo(ControlClock.advanceBy(Duration.ofMinutes(30)));

// Retomar fluxo normal do tempo
alice.attemptsTo(ControlClock.resume());

Rastreamento

Registrar rastreamentos detalhados para depuracao:

import net.serenitybdd.screenplay.playwright.interactions.tracing.StartTracing;
import net.serenitybdd.screenplay.playwright.interactions.tracing.StopTracing;

// Iniciar rastreamento com opcoes
alice.attemptsTo(
StartTracing.withScreenshots()
.andSnapshots()
.named("login-flow")
);

// ... executar acoes de teste ...

// Parar e salvar rastreamento
alice.attemptsTo(
StopTracing.andSaveTo(Paths.get("traces/login-flow.zip"))
);

// Visualizar rastreamento: npx playwright show-trace traces/login-flow.zip

Opcoes de rastreamento:

  • withScreenshots() - Incluir screenshots no rastreamento
  • andSnapshots() - Incluir snapshots do DOM
  • andSources() - Incluir arquivos de origem
  • named(String) - Definir nome do rastreamento

Persistencia de Estado de Sessao

Novo na versao 5.2.2

A persistencia de estado de sessao foi adicionada no Serenity BDD 5.2.2.

Salvar e restaurar estado de sessao do navegador (cookies, localStorage, sessionStorage) para acelerar testes e compartilhar sessoes autenticadas.

Salvando Estado de Sessao

import net.serenitybdd.screenplay.playwright.interactions.SaveSessionState;

// Salvar em um caminho especifico
Path sessionPath = Paths.get("target/sessions/authenticated.json");
alice.attemptsTo(
SaveSessionState.toPath(sessionPath)
);

// Salvar no local padrao com um nome
// Salva em: target/playwright/session-state/{name}.json
alice.attemptsTo(
SaveSessionState.toFile("admin-session")
);

Restaurando Estado de Sessao

import net.serenitybdd.screenplay.playwright.interactions.RestoreSessionState;

// Restaurar de um caminho especifico
alice.attemptsTo(
RestoreSessionState.fromPath(Paths.get("target/sessions/authenticated.json"))
);

// Restaurar do local padrao
alice.attemptsTo(
RestoreSessionState.fromFile("admin-session")
);

Caso de Uso: Reutilizando Sessoes Autenticadas

Um padrao comum e fazer login uma vez e reutilizar a sessao em multiplos testes:

public class AuthenticationSetup {

private static final Path SESSION_FILE = Paths.get("target/sessions/logged-in.json");

@BeforeAll
static void setupAuthenticatedSession() {
Actor setup = Actor.named("Setup")
.whoCan(BrowseTheWebWithPlaywright.usingTheDefaultConfiguration());

setup.attemptsTo(
Navigate.to("https://myapp.com/login"),
Enter.theValue("admin@example.com").into("#email"),
Enter.theValue("password123").into("#password"),
Click.on(Button.withText("Login")),

// Salvar a sessao autenticada
SaveSessionState.toPath(SESSION_FILE)
);

setup.wrapUp();
}
}

// Nos seus testes
@Test
void shouldAccessDashboard() {
alice.attemptsTo(
// Restaurar a sessao pre-autenticada
RestoreSessionState.fromPath(SESSION_FILE),

// Navegar diretamente para pagina protegida
Navigate.to("https://myapp.com/dashboard")
);

// Usuario ja esta logado!
assertThat(alice.asksFor(Text.of("h1"))).isEqualTo("Dashboard");
}

Conteudo do Estado de Sessao

O estado de sessao salvo e um arquivo JSON contendo:

  • Cookies: Todos os cookies para o contexto do navegador
  • Origins: Dados de localStorage e sessionStorage para cada origem

Isso permite:

  • Pular passos de login nos testes (execucao mais rapida)
  • Compartilhar autenticacao entre classes de teste
  • Criar fixtures de sessao para diferentes papeis de usuario
  • Testar Scenario de expiracao e atualizacao de sessao

Exemplo: Multiplos Papeis de Usuario

public class SessionFixtures {

public static void createAdminSession() {
// Login como admin e salvar sessao
Actor admin = createActor();
admin.attemptsTo(
Navigate.to("https://myapp.com/login"),
Enter.theValue("admin@example.com").into("#email"),
Enter.theValue("adminpass").into("#password"),
Click.on(Button.withText("Login")),
SaveSessionState.toFile("admin-session")
);
admin.wrapUp();
}

public static void createUserSession() {
// Login como usuario regular e salvar sessao
Actor user = createActor();
user.attemptsTo(
Navigate.to("https://myapp.com/login"),
Enter.theValue("user@example.com").into("#email"),
Enter.theValue("userpass").into("#password"),
Click.on(Button.withText("Login")),
SaveSessionState.toFile("user-session")
);
user.wrapUp();
}
}

// Em testes de admin
@Test
void adminShouldSeeAdminPanel() {
alice.attemptsTo(
RestoreSessionState.fromFile("admin-session"),
Navigate.to("https://myapp.com/admin")
);
assertThat(alice.asksFor(Presence.of("#admin-panel"))).isTrue();
}

// Em testes de usuario
@Test
void userShouldNotSeeAdminPanel() {
alice.attemptsTo(
RestoreSessionState.fromFile("user-session"),
Navigate.to("https://myapp.com/admin")
);
assertThat(alice.asksFor(Text.of("h1"))).isEqualTo("Access Denied");
}

Geracao de PDF

Gerar PDFs de paginas (apenas Chromium, modo headless):

import net.serenitybdd.screenplay.playwright.interactions.GeneratePDF;

// Geracao basica de PDF
alice.attemptsTo(
GeneratePDF.ofCurrentPage()
.andSaveTo(Paths.get("output/report.pdf"))
);

// Com opcoes
alice.attemptsTo(
GeneratePDF.ofCurrentPage()
.withFormat("A4")
.inLandscape()
.withMargins("1cm", "1cm", "1cm", "1cm")
.withHeaderTemplate("<div>Header</div>")
.withFooterTemplate("<div>Page <span class='pageNumber'></span></div>")
.displayHeaderAndFooter()
.printBackground()
.andSaveTo(Paths.get("output/report.pdf"))
);

Alternando Contextos

Frames

Trabalhar com iframes:

// Alternar para frame por nome ou ID
alice.attemptsTo(Switch.toFrame("payment-iframe"));

// Alternar para frame por Target
alice.attemptsTo(Switch.toFrame(Target.the("payment").locatedBy("#payment-frame")));

// Voltar para frame principal
alice.attemptsTo(Switch.toMainFrame());

Janelas e Abas

Lidar com multiplas janelas e abas:

// Alternar para nova janela/aba
alice.attemptsTo(Switch.toNewWindow());

// Fechar janela atual
alice.attemptsTo(CloseCurrentWindow.now());

Boas Praticas

Use Constantes Target

Defina Target como constantes para reutilizacao e manutenibilidade:

public class LoginPage {
public static Target EMAIL_FIELD = Target.the("email field")
.locatedBy("#email");
public static Target PASSWORD_FIELD = Target.the("password field")
.locatedBy("#password");
public static Target LOGIN_BUTTON = Target.the("login button")
.locatedBy("role=button[name='Log in']");
}

Prefira Seletores de Role

Use seletores de role ARIA para testes mais resilientes:

// Em vez de CSS
Target.the("Submit").locatedBy("button.primary-btn")

// Prefira seletor de role
Target.the("Submit").locatedBy("role=button[name='Submit']")

Use Fabricas de Elementos UI

Para elementos comuns, use as fabricas de elementos UI:

// Em vez de
Click.on(Target.the("Add button").locatedBy("role=button[name='Add to Cart']"))

// Use
Click.on(Button.withText("Add to Cart"))

Mock de Rede para Isolamento

Mock respostas de API para isolar testes de UI:

@BeforeEach
void setupMocks() {
actor.attemptsTo(
InterceptNetwork.requestsTo("**/api/products")
.andRespondWithJson(200, mockProducts)
);
}

Use Rastreamento para Depuracao

Habilite rastreamento ao depurar falhas de teste:

alice.attemptsTo(
StartTracing.withScreenshots().andSnapshots()
);

// Execute seu teste...

alice.attemptsTo(
StopTracing.andSaveTo(Paths.get("trace.zip"))
);
// Visualize com: npx playwright show-trace trace.zip

Tabelas de Referencia Rapida

Todas as Interaction

As tabelas a seguir fornecem uma referencia completa de todas as Interaction Playwright Screenplay disponiveis.

Interaction de Elementos

InteractionDescricaoExemplo
Click.on(target)Clicar em um elementoClick.on("#submit")
DoubleClick.on(target)Clicar duas vezes em um elementoDoubleClick.on("#item")
RightClick.on(target)Clicar com botao direito (menu de contexto) em um elementoRightClick.on("#file")
Hover.over(target)Mover mouse sobre um elementoHover.over("#menu")
Focus.on(target)Definir foco em um elementoFocus.on("#search")

Interaction de Entrada de Texto

InteractionDescricaoExemplo
Enter.theValue(text).into(target)Digitar texto em um campoEnter.theValue("john@test.com").into("#email")
Clear.field(target)Limpar um campo de entradaClear.field("#search")
Press.key(key)Pressionar uma tecla do tecladoPress.key("Enter")
Press.key(combo)Pressionar uma combinacao de teclasPress.key("Control+a")
Press.keys(keys...)Pressionar multiplas teclas em sequenciaPress.keys("Tab", "Tab", "Enter")

Interaction de Checkbox e Radio

InteractionDescricaoExemplo
Check.checkbox(target)Marcar um checkboxCheck.checkbox("#agree")
Uncheck.checkbox(target)Desmarcar um checkboxUncheck.checkbox("#newsletter")

Interaction de Dropdown

InteractionDescricaoExemplo
SelectFromOptions.byVisibleText(text).from(target)Selecionar por texto visivelSelectFromOptions.byVisibleText("Red").from("#color")
SelectFromOptions.byValue(value).from(target)Selecionar por atributo valueSelectFromOptions.byValue("red").from("#color")
SelectFromOptions.byIndex(index).from(target)Selecionar por indice (0-based)SelectFromOptions.byIndex(2).from("#color")
DeselectFromOptions.byValue(value).from(target)Desselecionar por valueDeselectFromOptions.byValue("red").from("#colors")
DeselectFromOptions.byVisibleText(text).from(target)Desselecionar por texto visivelDeselectFromOptions.byVisibleText("Red").from("#colors")
DeselectFromOptions.byIndex(index).from(target)Desselecionar por indiceDeselectFromOptions.byIndex(0).from("#colors")
DeselectFromOptions.all().from(target)Desselecionar todas as opcoesDeselectFromOptions.all().from("#colors")

Interaction de Rolagem

InteractionDescricaoExemplo
Scroll.to(target)Rolar elemento para visualizacaoScroll.to("#footer")
Scroll.to(target).andAlignToTop()Rolar com alinhamento no topoScroll.to("#section").andAlignToTop()
Scroll.to(target).andAlignToCenter()Rolar com alinhamento no centroScroll.to("#section").andAlignToCenter()
Scroll.to(target).andAlignToBottom()Rolar com alinhamento na baseScroll.to("#section").andAlignToBottom()
Scroll.toTop()Rolar para o topo da paginaScroll.toTop()
Scroll.toBottom()Rolar para a base da paginaScroll.toBottom()
Scroll.by(deltaX, deltaY)Rolar por quantidade em pixelsScroll.by(0, 500)
Scroll.toPosition(x, y)Rolar para posicao absolutaScroll.toPosition(0, 1000)

Interaction de Drag and Drop

InteractionDescricaoExemplo
Drag.from(source).to(target)Arrastar da origem para o destinoDrag.from("#item").to("#dropzone")
Drag.the(source).onto(target)Sintaxe fluente alternativaDrag.the("#card").onto("#column")

Interaction de Navegacao

InteractionDescricaoExemplo
Navigate.to(url)Navegar para uma URLNavigate.to("https://example.com")
Navigate.toTheBaseUrl()Navegar para URL base configuradaNavigate.toTheBaseUrl()

Interaction de Frame e Janela

InteractionDescricaoExemplo
Switch.toFrame(nameOrId)Alternar para iframe por nome/IDSwitch.toFrame("payment-iframe")
Switch.toFrame(target)Alternar para iframe por TargetSwitch.toFrame(PAYMENT_FRAME)
Switch.toMainFrame()Voltar para frame principalSwitch.toMainFrame()
Switch.toNewWindow()Alternar para nova janela/abaSwitch.toNewWindow()
CloseCurrentWindow.now()Fechar janela atualCloseCurrentWindow.now()

Interaction de Arquivo

InteractionDescricaoExemplo
Upload.file(path).to(target)Fazer upload de um arquivoUpload.file(Paths.get("doc.pdf")).to("#upload")
WaitForDownload.whilePerforming(action)Esperar download durante acaoWaitForDownload.whilePerforming(Click.on("#download"))
WaitForDownload...andSaveTo(path)Salvar download em caminhoWaitForDownload.whilePerforming(...).andSaveTo(path)

Interaction de JavaScript

InteractionDescricaoExemplo
Evaluate.javascript(script)Executar JavaScriptEvaluate.javascript("window.scrollTo(0,0)")

Interaction de Espera

InteractionDescricaoExemplo
WaitUntil.the(target).isVisible()Esperar visibilidade do elementoWaitUntil.the("#modal").isVisible()
WaitUntil.the(target).isNotVisible()Esperar elemento ficar ocultoWaitUntil.the("#spinner").isNotVisible()
WaitUntil.the(target).isHidden()Esperar elemento estar escondidoWaitUntil.the("#loading").isHidden()
WaitUntil...forNoMoreThan(duration)Definir timeout customizadoWaitUntil.the("#data").isVisible().forNoMoreThan(Duration.ofSeconds(10))

Interaction de Rede

InteractionDescricaoExemplo
InterceptNetwork.requestsTo(pattern).andRespondWith(options)Mock resposta com opcoesInterceptNetwork.requestsTo("**/api/**").andRespondWith(...)
InterceptNetwork.requestsTo(pattern).andRespondWithJson(status, data)Mock resposta JSONInterceptNetwork.requestsTo("**/users").andRespondWithJson(200, users)
InterceptNetwork.requestsTo(pattern).andHandle(handler)Handler de requisicao customizadoInterceptNetwork.requestsTo("**/api/**").andHandle(route -> ...)
InterceptNetwork.requestsTo(pattern).andAbort()Bloquear requisicoesInterceptNetwork.requestsTo("**/analytics/**").andAbort()
RemoveRoutes.all()Remover todos os handlers de rotaRemoveRoutes.all()
RemoveRoutes.forUrl(pattern)Remover rotas para padraoRemoveRoutes.forUrl("**/api/**")

Interaction de Dispositivo e Ambiente

InteractionDescricaoExemplo
EmulateDevice.device(name)Emular um dispositivoEmulateDevice.device("iPhone 14")
EmulateDevice.withViewport(width, height)Definir viewport customizadaEmulateDevice.withViewport(375, 812)
SetGeolocation.to(lat, lng)Definir geolocalizacaoSetGeolocation.to(51.5074, -0.1278)
SetGeolocation.toNewYork()Definir para Nova YorkSetGeolocation.toNewYork()
SetGeolocation.toLondon()Definir para LondresSetGeolocation.toLondon()
SetGeolocation.toTokyo()Definir para ToquioSetGeolocation.toTokyo()
SetGeolocation.clear()Limpar geolocalizacaoSetGeolocation.clear()
GrantPermissions.for_(permissions...)Conceder permissoes do navegadorGrantPermissions.for_("geolocation", "camera")
ClearPermissions.all()Limpar todas as permissoesClearPermissions.all()

Interaction de Controle de Relogio

InteractionDescricaoExemplo
ControlClock.install()Instalar relogio falsoControlClock.install()
ControlClock.setTo(instant)Definir relogio para hora especificaControlClock.setTo(Instant.parse("2024-01-15T10:30:00Z"))
ControlClock.advanceBy(duration)Avancar relogioControlClock.advanceBy(Duration.ofHours(2))
ControlClock.resume()Retomar fluxo normal do tempoControlClock.resume()

Interaction de Depuracao e Rastreamento

InteractionDescricaoExemplo
StartTracing.withScreenshots()Iniciar rastreamento com screenshotsStartTracing.withScreenshots()
StopTracing.andSaveTo(path)Parar e salvar rastreamentoStopTracing.andSaveTo(Paths.get("trace.zip"))
CaptureConsoleMessages.duringTest()Iniciar captura de consoleCaptureConsoleMessages.duringTest()
CaptureConsoleMessages.clear()Limpar mensagens capturadasCaptureConsoleMessages.clear()
CheckConsole.forErrors()Falhar se erros de console encontradosCheckConsole.forErrors()
CheckConsole.forWarnings()Falhar se avisos de console encontradosCheckConsole.forWarnings()
CaptureNetworkRequests.duringTest()Iniciar captura de redeCaptureNetworkRequests.duringTest()
CaptureNetworkRequests.clear()Limpar requisicoes capturadasCaptureNetworkRequests.clear()

Interaction de Estado de Sessao

InteractionDescricaoExemplo
SaveSessionState.toPath(path)Salvar sessao em caminho especificoSaveSessionState.toPath(Paths.get("session.json"))
SaveSessionState.toFile(name)Salvar sessao em local padraoSaveSessionState.toFile("admin-session")
RestoreSessionState.fromPath(path)Restaurar sessao de caminhoRestoreSessionState.fromPath(Paths.get("session.json"))
RestoreSessionState.fromFile(name)Restaurar sessao de local padraoRestoreSessionState.fromFile("admin-session")

Interaction de Requisicao de API

InteractionDescricaoExemplo
APIRequest.get(url)Fazer requisicao GETAPIRequest.get("https://api.example.com/users")
APIRequest.post(url)Fazer requisicao POSTAPIRequest.post("https://api.example.com/users")
APIRequest.put(url)Fazer requisicao PUTAPIRequest.put("https://api.example.com/users/1")
APIRequest.patch(url)Fazer requisicao PATCHAPIRequest.patch("https://api.example.com/users/1")
APIRequest.delete(url)Fazer requisicao DELETEAPIRequest.delete("https://api.example.com/users/1")
APIRequest...withJsonBody(object)Definir corpo JSON da requisicaoAPIRequest.post(url).withJsonBody(Map.of("name", "John"))

Interaction de Geracao de PDF

InteractionDescricaoExemplo
GeneratePDF.ofCurrentPage().andSaveTo(path)Gerar PDFGeneratePDF.ofCurrentPage().andSaveTo(Paths.get("page.pdf"))
GeneratePDF...withFormat(format)Definir formato do papelGeneratePDF.ofCurrentPage().withFormat("A4")
GeneratePDF...inLandscape()Usar orientacao paisagemGeneratePDF.ofCurrentPage().inLandscape()

Interaction de Testes Visuais

InteractionDescricaoExemplo
CompareScreenshot.ofPage().againstBaseline(name)Comparar pagina inteiraCompareScreenshot.ofPage().againstBaseline("home.png")
CompareScreenshot.of(target).againstBaseline(name)Comparar elementoCompareScreenshot.of("#card").againstBaseline("card.png")

Todas as Question

As tabelas a seguir fornecem uma referencia completa de todas as Question Playwright Screenplay disponiveis.

Question de Estado de Elemento

QuestionTipo de RetornoDescricaoExemplo
Presence.of(target)BooleanElemento existe no DOMactor.asksFor(Presence.of("#modal"))
Absence.of(target)BooleanElemento nao esta presenteactor.asksFor(Absence.of("#error"))
Visibility.of(target)BooleanElemento esta visivelactor.asksFor(Visibility.of("#popup"))
Enabled.of(target)BooleanElemento esta habilitadoactor.asksFor(Enabled.of("#submit"))
SelectedStatus.of(target)BooleanCheckbox/radio esta selecionadoactor.asksFor(SelectedStatus.of("#agree"))

Question de Conteudo de Elemento

QuestionTipo de RetornoDescricaoExemplo
Text.of(target)StringObter conteudo de texto do elementoactor.asksFor(Text.of("#title"))
Text.ofEach(target)List<String>Obter texto de todos os elementos correspondentesactor.asksFor(Text.ofEach(".item"))
Value.of(target)StringObter valor do campo de entradaactor.asksFor(Value.of("#email"))
Attribute.of(target).named(attr)StringObter valor do atributoactor.asksFor(Attribute.of("#link").named("href"))
CSSValue.of(target).named(prop)StringObter valor da propriedade CSSactor.asksFor(CSSValue.of("#box").named("color"))

Question de Informacao da Pagina

QuestionTipo de RetornoDescricaoExemplo
CurrentUrl.ofThePage()StringObter URL atual da paginaactor.asksFor(CurrentUrl.ofThePage())
PageTitle.ofThePage()StringObter titulo da paginaactor.asksFor(PageTitle.ofThePage())

Question de Download

QuestionTipo de RetornoDescricaoExemplo
DownloadedFile.suggestedFilename()StringObter nome de arquivo sugeridoactor.asksFor(DownloadedFile.suggestedFilename())
DownloadedFile.url()StringObter URL do downloadactor.asksFor(DownloadedFile.url())
DownloadedFile.path()PathObter caminho do arquivo baixadoactor.asksFor(DownloadedFile.path())
DownloadedFile.failure()StringObter razao da falha (null se sucesso)actor.asksFor(DownloadedFile.failure())

Question de Mensagem do Console

QuestionTipo de RetornoDescricaoExemplo
ConsoleMessages.all()List<String>Obter todas as mensagens do consoleactor.asksFor(ConsoleMessages.all())
ConsoleMessages.errors()List<String>Obter erros do consoleactor.asksFor(ConsoleMessages.errors())
ConsoleMessages.warnings()List<String>Obter avisos do consoleactor.asksFor(ConsoleMessages.warnings())
ConsoleMessages.count()IntegerObter contagem total de mensagensactor.asksFor(ConsoleMessages.count())

Question de Acessibilidade

QuestionTipo de RetornoDescricaoExemplo
AccessibilitySnapshot.ofThePage()StringObter arvore de acessibilidade da paginaactor.asksFor(AccessibilitySnapshot.ofThePage())
AccessibilitySnapshot.of(target)StringObter arvore de acessibilidade do elementoactor.asksFor(AccessibilitySnapshot.of("#nav"))

Question de Requisicao de Rede

QuestionTipo de RetornoDescricaoExemplo
NetworkRequests.all()List<CapturedRequest>Obter todas as requisicoes capturadasactor.asksFor(NetworkRequests.all())
NetworkRequests.count()IntegerObter contagem total de requisicoesactor.asksFor(NetworkRequests.count())
NetworkRequests.failed()List<CapturedRequest>Obter requisicoes falhasactor.asksFor(NetworkRequests.failed())

Question de Resposta de API

QuestionTipo de RetornoDescricaoExemplo
LastAPIResponse.statusCode()IntegerObter codigo de status da respostaactor.asksFor(LastAPIResponse.statusCode())
LastAPIResponse.ok()BooleanVerificar se status e 2xxactor.asksFor(LastAPIResponse.ok())
LastAPIResponse.body()StringObter corpo da resposta como stringactor.asksFor(LastAPIResponse.body())
LastAPIResponse.jsonBody()Map<String, Object>Parsear resposta JSON como Mapactor.asksFor(LastAPIResponse.jsonBody())

Fabricas de Elementos UI

Classes de fabrica para localizar elementos UI comuns.

FabricaMetodosExemplo
ButtonwithText(text), withNameOrId(id), withAriaLabel(label), containingText(text), locatedBy(selector)Button.withText("Submit")
InputFieldwithNameOrId(id), withPlaceholder(text), withLabel(label), withAriaLabel(label), locatedBy(selector)InputField.withPlaceholder("Email")
LinkwithText(text), containingText(text), withTitle(title), locatedBy(selector)Link.withText("Learn more")
CheckboxwithLabel(label), withNameOrId(id), withValue(value), locatedBy(selector)Checkbox.withLabel("I agree")
RadioButtonwithLabel(label), withNameOrId(id), withValue(value), locatedBy(selector)RadioButton.withValue("express")
DropdownwithLabel(label), withNameOrId(id), locatedBy(selector)Dropdown.withLabel("Country")
LabelwithText(text), withExactText(text), forFieldId(id), locatedBy(selector)Label.forFieldId("email")
ImagewithAltText(alt), withSrc(src), withSrcContaining(text), locatedBy(selector)Image.withAltText("Logo")

Migracao do WebDriver

Ao migrar do serenity-screenplay-webdriver:

  1. Mudanca de Ability:

    • Substitua BrowseTheWeb por BrowseTheWebWithPlaywright
  2. Sintaxe de Seletores:

    • CSS e XPath funcionam de forma semelhante
    • Adicione seletores de role para testes mais robustos
    • Use >> para encadear seletores em vez de aninhar
  3. Espera:

    • Playwright espera automaticamente; esperas explicitas sao menos necessarias
    • Remova a maioria das chamadas WaitUntil
  4. Metodos de Localizacao:

    • By.id("x") se torna "#x"
    • By.cssSelector("x") se torna "x"
    • By.xpath("x") se torna "xpath=x"
  5. Novas Capacidades:

    • Use interceptacao de rede para mock de APIs
    • Use rastreamento para depuracao
    • Use emulacao de dispositivos para testes responsivos

Exemplo Completo

import net.serenitybdd.screenplay.Actor;
import net.serenitybdd.screenplay.playwright.abilities.BrowseTheWebWithPlaywright;
import net.serenitybdd.screenplay.playwright.interactions.*;
import net.serenitybdd.screenplay.playwright.questions.*;
import net.serenitybdd.screenplay.playwright.ui.*;
import static org.assertj.core.api.Assertions.assertThat;

public class ShoppingCartTest {

Actor alice = Actor.named("Alice");

@BeforeEach
void setup() {
Playwright playwright = Playwright.create();
Browser browser = playwright.chromium().launch();
alice.can(BrowseTheWebWithPlaywright.using(browser));
}

@Test
void shouldAddItemToCart() {
alice.attemptsTo(
Navigate.to("https://shop.example.com"),
Click.on(Button.withText("Electronics")),
Click.on(Link.containingText("Laptop")),
SelectFromOptions.byVisibleText("16GB").from(Dropdown.withLabel("Memory")),
Click.on(Button.withText("Add to Cart"))
);

String cartCount = alice.asksFor(Text.of("#cart-count"));
assertThat(cartCount).isEqualTo("1");

List<String> cartItems = alice.asksFor(Text.ofEach(".cart-item-name"));
assertThat(cartItems).contains("Laptop");
}
}

Leitura Adicional