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​