Automação de Teste com Playwright
Vanilton Pinheiro
Version 1.36
Version 1.25
Quem sou eu?
Vanilton Pinheiro
Analista de Sistemas
Quem são vocês?
O que trás vocês até aqui?
# O que vamos precisar
Ambiente
- Python 3.7+
- IDE PyCharm CE ou Professional
- Docker
- SO:
- Windows
- Linux
- macOS
1
Introdução Automação de Teste
2
Entendendo a Estrutura de Teste com Unittest
3
Conhecendo o Playwright
5
Funções, Eventos
4
Localizadores
6
Inspetor e Gerador de Teste e Relatórios
# O que veremos
#1
Introdução Automação de Teste
Por quê automatizar?
# 1 Introdução Automação de Teste
- Os testes automatizados tem objetivo principal de reduzir esforço em testes manuais.
- Feedback rápido
- Redução de Bugs
- Confiança na solução
- Produtividade
O que é automação de teste?
Automação de teste é o uso de software para controlar a execução do teste de software, a comparação dos resultados esperados com os resultados reais, a configuração das pré-condições de teste e outras funções de controle e relatório de teste.
# 1 Introdução Automação de Teste
Fonte: https://pt.wikipedia.org/wiki/Automa%C3%A7%C3%A3o_de_teste
O que automatizar?
# 1 Introdução Automação de Teste
- O que garante o valor de negócio esperado pelo usuário (fórmulas, áreas críticas)
- O que não necessita da avaliação por percepção humana
- O que simula realmente os passos do usuário
Quando automatizar?
- Quando existe ganho de custo de tempo a médio ou longo prazo frente o teste manual
- Quando tarefas manuais se tornam complexas com o tempo
- Quando se possui muitos ambientes ou fatores de teste
- Quando existe estabilidade na área a ser testada
# 1 Introdução Automação de Teste
Quando não automatizar?
- Quando existe instabilidade na área a ser testada
- Limitação ou dependência de ferramenta
- Quando o custo de pessoas especialistas para a atividade é alto
- Quando o tempo de implementação for maior que o tempo ganho na repetição do teste
# 1 Introdução Automação de Teste
Como automatizar?
# 1 Introdução Automação de Teste
Lento
Rápido
$$$
$
# de testes
- Automação de API
- Contrato ou Mock
- Unitário
- Automação Funcional
Como automatizar?
- Deve ser obrigatoriamente implementado através de alguma ferramenta que simule as operações do usuário.
- Deve-se estruturar o teste verificando o resultado esperado com o obtido.
# 1 Introdução Automação de Teste
#2
Entendendo a Estrutura de Teste com Unittest
Teste Unitário
# 2 Entendendo a Estrutura de Teste com Unittest
O teste unitário consiste em verificar o comportamento das menores unidades em sua aplicação.
Tecnicamente, isso seria uma classe ou até mesmo um método de classe em línguas orientadas a objetos, e seria um procedimento ou função em línguas processuais e funcionais.
Teste Unitário
# 2 Entendendo a Estrutura de Teste com Unittest
-
Cada teste deve lidar apenas com:
Um objective — Um cenário a ser testado (um “dado”).
Uma action — Um método para testar (um “quando”).
Um assert — Uma chamada para um método de verificação (um “então”).
LEGAL!
MAS COMO SE FAZ ESSES TESTES NA PRÁTICA?
Framework Teste Unitário
# 2 Entendendo a Estrutura de Teste com Unittest
Python Unittest
# 2 Entendendo a Estrutura de Teste com Unittest
Unittest o foi originalmente inspirada no JUnit e tem uma semelhança às principais estruturas de teste de unidade em outras linguagens. Ele suporta automação de teste, compartilhamento de código de configuração e desligamento para testes, agregação de testes em coleções e independência dos testes da estrutura de relatórios.
Python Unittest - Criando Template
# 2 Entendendo a Estrutura de Teste com Unittest
Na IDE PyCharm:
- Criar um novo Projeto
- Adicionar um python file que já é oferecido o modelo abaixo:
Python Unittest - Importante saber
# 2 Entendendo a Estrutura de Teste com Unittest
- O Unittest utiliza da orientação a objeto
- Uma classe de teste com Unittest
- Herda de unittest.TestCase
- Pode conter vários casos de teste.
-
Um caso de teste é um método dentro dessa classe com nome iniciado de test. Ex: test_cadastro_xpto()
Python Unittest - Importante saber
# 2 Entendendo a Estrutura de Teste com Unittest
No corpo de um método de teste basicamente o que se faz é:
-
Criar um objeto da classe que contem o método a ser testado (se o método a ser testado for um método de instância)
-
Invocar o método em teste com os parâmetros desejados e armazenar o valor de retorno
-
Utilizar uma das asserções disponíveis no framework para comparar o resultado obtido com o resultado esperado.
Python Unittest - Importante saber
# 2 Entendendo a Estrutura de Teste com Unittest
Para a criação de um caso de teste e necessário:
-
Identificar o método a ser testado
-
Compreender a especificação do método: o que recebe de entrada e qual a saída produzida em função da entrada escolhida.
-
Comparar a saída produzida (obtida) com aquela que deveria ser gerada conforme a especificação.
Dicas escrita de teste
# 2 Entendendo a Estrutura de Teste com Unittest
-
É uma boa prática de escrita de casos de teste unitário, separar cada caso de teste em um método de teste.
-
Da mesma forma, deve-se agrupar em uma classe de teste, os casos de teste referentes a determinada classe e/ou método em teste, visando facilitar a localização dos testes.
Prática
# 2 Entendendo a Estrutura de Teste com Unittest
-
Clonar / baixar a branch main em https://github.com/Vanilton18/xpto-exemplos-teste
-
Verifique o comportamento do método check_valid_identifier na Classe Validators no pacote apps
-
Criar a classe de Teste no pacote test_apps e implemente os cenários de acordo com a especificação do método.
Prática
# 2 Entendendo a Estrutura de Teste com Unittest
-
Verifique o comportamento do método check_password na Classe Validators no pacote apps
-
Criar a classe de Teste no pacote test_apps e implemente os cenários de acordo com a especificação do método.
#3
Conhecendo o Playwright
Por quê?
# 3 Conhecendo o Playwright
-
O Playwright foi criado especificamente para acomodar as necessidades de testes de ponta a ponta, famoso e2e.
O que é?
# 3 Conhecendo o Playwright
-
É uma biblioteca Node para automatizar os navegadores Chromium, WebKit e Firefox com uma única API. Ele permite a automação da Web entre navegadores de modo confiável e rápido.
Como faz?
# 3 Conhecendo o Playwright
-
Playwright fornece automação entre navegadores por meio de uma única API.
-
O Playwright inicia navegadores sem periféricos (headless) por padrão.
-
A abordagem usada pelo Playwright será familiar para os usuários de outras estruturas de teste do navegador, como Selenium WebDriver.
# 3 Conhecendo o Playwright
Quem usa?
08/2022
06/2023
Atualizado em: 10/06/2023
O que suporta?
# 3 Conhecendo o Playwright
>=3.7
# 3 Conhecendo o Playwright
- Taxa de transferência = Gasta menos ciclos de CPU
- Latência = Executar teste rápido
- Fragilidade = Execuções confiáveis
Benefícios chaves
Mão na massa!
# 3 Conhecendo o Playwright
# 3 Conhecendo o Playwright
Instalação
pip install --upgrade pip
pip install playwright
playwright install
Vamos criar um projeto Python puro e instalar as dependências:
# 3 Conhecendo o Playwright
Criando Projeto e Instalando Biblioteca
mkdir projeto-playwright # Criar pasta do projeto
python -m venv venv # Criar ambiente virtual
pip install --upgrade pip # Atualizar gerenciador de pacotes
pip install playwright # Instalar biblioteca da ferramenta
playwright install chromium # Instalar navegador Chromium (chromium, firefox, webkit)
playwright install # Instalar todos navegadores
Vamos criar um projeto Python e instalar as dependências:
# 3 Conhecendo o Playwright
Opções Playwright - Ajuda
playwright --help # Opções do executor Playwright
playwright --version # Versão instalada
Comandos:
open [options] [url] # abrir página no navegador especificado via -b, --browser
codegen [options] [url] # abrir página e gerar código para ações do usuário
cr [options] [url] # abrir página no Chromium
ff [options] [url] # abrir página no Firefox
wk [options] [url] # abrir página no WebKit
screenshot [options] <url> <filename> # Capturar tela
pdf [options] <url> <filename> # Salvar página como pdf
show-trace [options] [trace...] # Mostrar visualizador de rastreamento
# 3 Conhecendo o Playwright
Vamos praticar
#Sigla de navegadores
# cr -> chromium, ff -> firefox, wk -> webkit
# Exemplos de uso via CLI
playwright --help # Opções do executor Playwright
playwright open -b cr https://vanilton.net # Abrir navegador chrome em um site
playwright open --help # Opções do comando open
playwright pdf --timeout 50000 https://google.com meu_pdf.pdf
# Gerar PDF da página
playwright screenshot --timeout 30000 http://vanilton.net ss_full.png
# Gerar imagem PNG da página com um determinado tamanho de tela
playwright screenshot --timeout 30000 --viewport-size "720, 1280" https://vanilton.net ss_portrait.png
# Gravar passos de teste
playwright codegen https://vanilton.net --save-trace trace.zip
# Abrir um trace gravado
playwright show-trace trace.zip
# 3 Conhecendo o Playwright
O MVP para automatizar
1. Navegar
- Abrir um navegador
- Acessar uma página web
- Voltar uma página
- Capturar texto da página
- Ler uma URL
- Ler título de uma página
3. Capturar
- Clicar em botões, links e outros elementos
- Preencher campos
- Selecionar elementos
2. Interagir
4. Esperar
- Aguardar a exibição de elementos
# 3 Conhecendo o Playwright
Comandos para Navegar
# Acessar a url
page.goto(url, **kwargs)
# Atualizar a página (F5)
page.reload(**kwargs)
# Voltar uma página no histórico
page.go_back(**kwargs)
# Avançar uma página no histórico
page.go_forward(**kwargs)
# 3 Conhecendo o Playwright
Prática de navegação
from playwright.sync_api import sync_playwright
browser = sync_playwright().start().chromium.launch()
page = browser.new_page()
page.goto("https://google.com")
print("Acessado -> " + page.title())
page.goto("http://vanilton.net")
print("Acessado -> " + page.title())
page.reload()
print("Atualizando página")
page.go_back()
print("Retornando ao google")
page.go_forward()
print("Avançando ao vanilton.net")
print("Acessado -> " + page.title())
Experimente executar o código abaixo:
# 3 Conhecendo o Playwright
Comandos para Interagir
# Preencher
page.locator(CSS).fill("Vanilton Pinheiro")
# Clicar
page.locator(XPATH).click()
# Selecionar um elemento
page.select_option('select#colors', 'blue')
# Selecionar múltiplos elementos
page.select_option('select#colors', ['red', 'green', 'blue'])
# 3 Conhecendo o Playwright
Prática para interagir e capturar
from playwright.sync_api import sync_playwright
browser = sync_playwright().start().chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://google.com")
# Preencher campo de pesquisa
page.locator('[name="q"]').fill("Vanilton Pinheiro")
# Capturar informações da página
print(page.title())
print(page.inner_html('html'))
print("valor? -> " + page.locator('[name="q"]').input_value())
print("oculto? -> " + page.locator('[name="q"]').is_hidden().__str__())
print("visível? -> " + page.locator('[name="q"]').is_visible().__str__())
print("disponível? -> " + page.locator('[name="q"]').is_enabled().__str__())
print("Valor Atributo Aria Label? -> " + page.locator('[name="q"]').get_attribute("aria-label"))
# Fechar página
page.close()
Experimentem executar o código abaixo:
#4
Localizadores
# 4 Localizadores
O que é um localizador?
Toda e qualquer ferramenta que realiza automatização, seja uma página web, um aplicativo mobile ou desktop, necessita de uma estratégia para identificar o elemento ao qual se deseja interagir, eis o que chamamos de localizador.
# 4 Localizadores
Localizador Web - DOM
# 4 Localizadores
Localizadores Playwright
page.locator(selector, **kwargs)
O Playwright suporta muitos seletores diferentes, como Text, CSS, XPath e muitos mais.
Argumentos opcionais
# 4 Localizadores
Localizadores Playwright
locator = page.locator("text=Submit")
locator.hover()
locator.click()
Toda vez que o localizador é usado para alguma ação, o elemento DOM atualizado é localizado na página. Portanto, no trecho abaixo, o elemento DOM subjacente será localizado duas vezes, antes de cada ação
# 4 Localizadores
Localizadores Playwright
O Playwright a partir da versão 1.27 adotou localizadores padrões, todos retornando um objeto Locator da API Playwrigth.
Atualizado em: 13/06/2023
# 4 Localizadores
Localizadores Playwright - Padrões
#para localizar por atributos de acessibilidade explícitos e implícitos.
page.get_by_role()
#para localizar por conteúdo de texto.
page.get_by_text()
#para localizar um controle de formulário pelo texto do rótulo associado.
page.get_by_label()
#para localizar uma entrada por espaço reservado.
page.get_by_placeholder()
#para localizar um elemento, geralmente imagem, por sua alternativa de texto.
page.get_by_alt_text()
#para localizar um elemento por seu atributo de título.
page.get_by_title()
#para localizar um elemento com base em seu atributo data-testid
page.get_by_test_id()
Atualizado em: 13/06/2023
# 4 Localizadores
Localizadores Playwright
O localizador get_by_role tem sua utilização sugerida para elementos que possuem interação como botões, checkboxes e radios e etc.
Atualizado em: 15/06/2023
# 4 Localizadores
Localizadores Playwright - Prática
locator = page.get_by_role("button", name="Nome")
locator.click()
page.get_by_role("checkbox", name="Moto").check()
page.get_by_role("radio", name="M").check()
page.get_by_role("checkbox", name=re.compile("moto", re.IGNORECASE)).check()
Atualizado em: 15/06/2023
- Acesse: https://vanilton.net/web-test/input-types/
- Experimente a utilização dos exemplos abaixo.
# 4 Localizadores
Localizadores Playwright
# Corresponde <span>
page.get_by_text("meu querido")
# Corresponde <div>
page.get_by_text("Olá meu querido")
# Corresponde segunda <div>
page.get_by_text("Olá", exact=True)
# Corresponde entre <div>s
page.get_by_text(re.compile("Olá"))
# Corresponde segunda <div>
page.get_by_text(re.compile("^olá$", re.IGNORECASE))
Localizadores get_by_text sugere-se utilização para elementos que não possuem interação como div, span, p, h1 e etc.
Você pode localizar por substring de texto, string exata ou uma expressão regular:
Atualizado em: 15/06/2023
<div>Olá
<span>meu querido
</span>
</div>
<div>Olá</div>
# 4 Localizadores
Localizadores Playwright
page.get_by_title("message-area")
Localizadores get_by_title identificam elementos por seu atributo de título.
<form id="form-login">
<label for="username">First name:</label><br>
<input type="text" id="username" name="username" maxlength="30" required=""><br>
<label for="password">Password:</label><br>
<input type="password" id="password" required=""><br>
<input type="submit" class="btn-primary" onclick="Enviar();" value="Autenticar"><br>
<span id="message" title="message-area">Credenciais inválidas. Tente novamente.</span>
</form>
Atualizado em: 30/06/2023
Se deve encontrar uma correspondência exata: com distinção entre maiúsculas e minúsculas e string inteira.
page.get_by_title("parametro", exact=True).fill("xpto")
# 4 Localizadores
Localizadores Playwright - Prática
- Acesse: https://vanilton.net/web-test/text/
- Experimente realizar um login inválido e verificar a existência da mensagem abaixo por meio do localizador get_by_title e get_by_text.
# 4 Localizadores
Localizadores Playwright
page.get_by_label("First name:").fill("juju")
Localizadores get_by_label permitem identificar elementos de entrada pelo texto do elemento <label> ou aria-labelledby associado, ou pelo atributo aria-label.
<form id="form-login">
<label for="username">First name:</label><br>
<input type="text" id="username" name="username" maxlength="30" required=""><br>
<label for="password">Password:</label><br>
<input type="password" id="password" required=""><br>
<input type="submit" class="btn-primary" onclick="Enviar();" value="Autenticar"><br>
<span id="message" title="message-area">Credenciais inválidas. Tente novamente.</span>
</form>
Se deseja encontrar uma correspondência exata: com distinção entre maiúsculas e minúsculas e string inteira.
Atualizado em: 30/06/2023
page.get_by_label("parametro", exact=True).fill("xpto")
# 4 Localizadores
Localizadores Playwright
page.get_by_placeholder("admin").fill("jose")
Localizadores get_by_placeholder permitem identificar elementos de entrada pelo texto do elemento <label> ou aria-labelledby associado, ou pelo atributo aria-label.
<form id="form-login">
<label for="username">First name:</label><br>
<input type="text" id="username" name="username" maxlength="30" required="" placeholder="admin"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" required=""><br>
<input type="submit" class="btn-primary" onclick="Enviar();" value="Autenticar"><br>
<span id="message" title="message-area">Credenciais inválidas. Tente novamente.</span>
</form>
Atualizado em: 30/06/2023
Se deseja encontrar uma correspondência exata: com distinção entre maiúsculas e minúsculas e string inteira.
page.get_by_placeholder("admin", exact=True).fill("xpto")
# 4 Localizadores
Localizadores Playwright
Todas as imagens devem ter um atributo alt que descreva a imagem. Você pode localizar uma imagem com base na alternativa de texto usando page.get_by_alt_text().
page.get_by_alt_text("triângulo equilátero")
<img src="equilatero.png" alt="triângulo equilátero">
# 4 Localizadores
Localizadores Playwright - Prática
- Acesse: https://vanilton.net/web-test/triangulo
- Faça o fluxo para identificar:
- Triângulos válidos:
- escaleno
- equilátero
- isósceles
- Triângulo inválido
- Triângulos válidos:
- Utilize o método get_by_alt_text para identificar a correspondência de imagem exibida no triângulo
# 4 Localizadores
Localizadores Playwright - Prática
- Acesse: https://vanilton.net/web-test/triangulo_v2/
- Implemente os testes realizados na versão 1 do triângulo.
# 4 Localizadores
Localizadores Text
page.locator("text=Atualizar").click()
page.locator("text=atualiz").click()
page.locator("text='Atualizar Página'").click()
page.locator("text='atualizar página'").click()
page.locator("text='Download'").click()
page.locator("input", has_text="Autenticar")
- O seletor de texto localiza os elementos que contêm o texto passado.
- O seletor de texto tem algumas variações:
<html>
<body>
<h2>Log in</h2>
<div>
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname">
<input type="submit" class="btn-primary"
onclick="Enviar();" value="Enviar" />
</form>
<button>Atualizar Página</button>
<button>Download Relatório A</button>
</div>
</body>
</html>
A correspondência padrão não diferencia maiúsculas de minúsculas e procura uma substring
Modo exato 'valor' entre aspas, implica correspondência com distinção entre maiúsculas e minúsculas
Elemento contendo outro elemento por texto
# 4 Localizadores
Localizadores Text
Fonte: https://regex101.com/
page.locator("text=/s*Relatório [A-Z]/i").inner_text()
Modo JavaScript Regex
# 4 Localizadores
Localizadores CSS
# Clica no primeiro button que encontrar
page.locator("button").click()
# Clica no primeiro button com visibilidade
page.locator("button:visible").click()
page.locator("button >> visible=true").click()
- O mecanismo CSS acessa o DOM aberto por padrão.
- O Playwright adiciona pseudo-classes personalizadas como :visible, :text e muito mais.
<button style='display: none'>Invisible</button>
<button>Visible</button>
# 4 Localizadores
Localizadores CSS - Prática
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/web-test/display/")
page.locator('.circle').click()
print(page.locator('#circle').get_attribute("display"))
- Acesse: http://vanilton.net/web-test/display/
- Implemente clicar no círculo visível
3. Implemente clicar no botão Atualizar Página
# 4 Localizadores
Localizadores CSS
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/web-test/display/")
page.locator("section", has=page.locator(".circle >> visible=true")).click()
print(page.locator('#circle').get_attribute("display"))
Ainda seguindo o exemplo anterior, os localizadores suportam uma opção para selecionar apenas elementos que tenham um descendente que corresponda a outro localizador.
Observe que o localizador interno é correspondido a partir do externo, não da raiz do documento.
# 4 Localizadores
Localizadores CSS - Prática
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/web-test/display/")
print(page.locator("section:has(div.circle)").inner_text())
print(page.locator('#circle').get_attribute("display"))
Seguindo o exemplo anterior, a pseudoclasse :has() é uma pseudoclasse CSS experimental. Ele retorna um elemento se algum dos seletores passados como parâmetros relativos ao :scope do elemento fornecido corresponder a pelo menos um elemento.
Implemente a localização da seção de Opções
# 4 Localizadores
Localizadores CSS - Prática
# Listando seções
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/web-test/display/")
sections = page.locator("section")
print(sections.all_inner_texts())
print(sections.locator("h4", has_text="Op").inner_text())
print(page.locator('#circle').get_attribute("display"))
-
Aumentando localizadores existentes
Implemente pegar a seção pelo seu índice
# 4 Localizadores
Localizadores CSS
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/web-test/display/")
# A lista separada por vírgulas de seletores CSS corresponderá a todos os
# elementos que podem ser selecionados por um dos seletores dessa lista.
page.locator('button:has-text("Show Hide Elements"), button:has-text("Exibir Elementos Ocultos")').click()
print(page.locator('#show-element').inner_text())
page.locator('button:has-text("Show Hide Elements"), button:has-text("Exibir Elementos Ocultos")').click()
print(page.locator('#show-element').inner_text())
-
Selecionando elementos que correspondem a uma das condições (OR)
Onde esta solução pode ser interessante?
# 4 Localizadores
Localizadores XPATH
-
XPath pode ser usado para navegar pelos elementos e atributos em um documento XML.
-
XPath usa expressões para selecionar nós ou conjuntos de nós em um documento XML. Essas expressões se parecem muito com as utilizadas em sistemas de arquivos de computador.
# 4 Localizadores
Localizadores XPATH
-
XPath significa XML Path Language
-
XPath usa a sintaxe "path like" para identificar e navegar em nós em um documento
-
XML XPath contém mais de 200 funções integradas
-
XPath é um elemento importante no padrão XSLT
-
XPath é uma recomendação do W3C
-
Arquivos HTML seguem o padrão XML, logo...
# 4 Localizadores
Localizadores XPATH
-
Os seletores XPath são equivalentes a chamar Document.evaluate. Exemplo: xpath=//html/body.
-
O seletor começando com // ou .. é considerado um seletor xpath. Por exemplo, o Playwright converte '//html/body' para 'xpath=//html/body'.
-
XPATH não acessa Shadow DOM
# 4 Localizadores
Ferramenta para auxiliar criação de Xpath
Inspect Chrome
Utilize CTRL + F para ativar
Clique com o direito do mouse para copiar caminhos xpath
# 4 Localizadores
Localizadores XPATH - Selecionando
/ Seleciona do nó raiz
// Seleciona os nós no documento que correspondem à seleção, não importa onde eles estejam
. Seleciona o nó atual
.. Seleciona o pai do nó atual
@ Seleciona atributos
* Corresponde a qualquer nó de elemento
# 4 Localizadores
Localizadores XPATH - Expressões
<html>
<head>
<meta charset="utf-8">
<title>Exemplo de Visualização Elementos</title>
</head>
<body>
<h3>Clique em um elemento para ocultar</h3>
<section>
<h4>Formas</h4>
<div class="circle" style="display: none" id="circle"></div>
<div class="circle" id="circle-clone"></div>
<div class="rounded" id="rounded"></div>
<div class="square" id="square"></div>
</section>
<section>
<h4>Opções</h4>
<button style="display: none">Atualizar Página</button>
<button id='refresh-page' onclick="AtualizarPagina()">Atualizar Página</button>
<button id="show-element">Exibir Elementos Ocultos</button>
</section>
</body>
</html>
/html # Selecionar o elemento html a partir da raiz da árvore DOM
//h4 # Selecionar todos elementos h4 a partir da raiz da árvore DOM de modo descendente
//h4/.. # Selecionar todos elementos acima de um h4
//@style # Selecionar todos elementos com um atributo style
# 4 Localizadores
Localizadores XPATH - Prática
/html # Selecionar o elemento html a partir da raiz da árvore DOM
//h4 # Selecionar todos elementos h4 a partir da raiz da árvore DOM de modo descendente
//h4/.. # Selecionar todos elementos acima de um h4
//@style # Selecionar todos elementos com um atributo style
- Implemente um localizador para cada expressão acima retornando o seu conteúdo de texto em um print()
# 4 Localizadores
Localizadores XPATH - Predicados
<html>
<head>
<meta charset="utf-8">
<title>Exemplo de Visualização Elementos</title>
</head>
<body>
<h3>Clique em um elemento para ocultar</h3>
<section>
<h4>Formas</h4>
<div class="circle" style="display: none" id="circle"></div>
<div class="circle" id="circle-clone"></div>
<div class="rounded" id="rounded"></div>
<div class="square" id="square"></div>
</section>
<section>
<h4>Opções</h4>
<button style="display: none">Atualizar Página</button>
<button id='refresh-page' onclick="AtualizarPagina()">Atualizar Página</button>
<button id="show-element">Exibir Elementos Ocultos</button>
</section>
</body>
</html>
# Os predicados [] são usados para localizar um nó específico ou um nó que contém um valor específico.
//section[1] # Selecionar o primeiro elemento section
//section[last()] # Selecionar a última section
//section[last()-1] # Selecionar a penúltina section
//div[@class="rounded"] # Selecionar uma div com classe de nome rounded
//section/*[position()>3] # Selecionar todos elementos filhos de section a partir do terceiro nó
//*[@onclick] # Selecionar todos elementos com um atributo onclick
# 4 Localizadores
Localizadores XPATH - Prática
# Os predicados [] são usados para localizar um nó específico ou um nó que contém um valor específico.
//section[1] # Selecionar o primeiro elemento section
//section[last()] # Selecionar a última section
//section[last()-1] # Selecionar a penúltina section
//div[@class="rounded"] # Selecionar uma div com classe de nome rounded
//section/*[position()>3] # Selecionar todos elementos filhos de section a partir do terceiro nó
//*[@onclick] # Selecionar todos elementos com um atributo onclick
- Implemente um localizador para cada expressão acima retornando o seu conteúdo de texto em um print()
# 4 Localizadores
Localizadores XPATH - Funções
<html>
<head>
<meta charset="utf-8">
<title>Exemplo de Visualização Elementos</title>
</head>
<body>
<h3>Clique em um elemento para ocultar</h3>
<section>
<h4>Formas</h4>
<div class="circle" style="display: none" id="circle"></div>
<div class="circle" id="circle-clone"></div>
<div class="rounded" id="rounded"></div>
<div class="square" id="square"></div>
</section>
<section>
<h4>Opções</h4>
<button style="display: none">Atualizar Página</button>
<button id='refresh-page' onclick="AtualizarPagina()">Atualizar Página</button>
<button id="show-element">Exibir Elementos Ocultos</button>
</section>
</body>
</html>
# Funções de predicados
//button[contains(.,'Exibir')] # Retorna um ou mais botões contendo o texto "Exibir".
//button[contains(.,'Exibir')] | //button[contains(.,'Hide')] # Selecionando vários caminhos
//button[text()='Show Hide Elements'] # Seleciona um ou mais botões com o nome exato 'Show Hide Elements'
//button[@id and @onclick]
# 4 Localizadores
Localizadores XPATH - Exercício
# Funções de predicados
//button[contains(.,'Exibir')] # Retorna um ou mais botões contendo o texto "Exibir".
//button[contains(.,'Exibir')] | //button[contains(.,'Hide')] # Selecionando vários caminhos
//button[text()='Show Hide Elements'] # Seleciona um ou mais botões com o nome exato 'Show Hide Elements'
//button[@id and @onclick]
- Implemente um localizador para cada expressão acima retornando o seu conteúdo de texto em um print()
# 4 Localizadores
Localizadores XPATH - Axes
<html>
<head>
<meta charset="utf-8">
<title>Exemplo de Visualização Elementos</title>
</head>
<body>
<h3>Clique em um elemento para ocultar</h3>
<section>
<h4>Formas</h4>
<div class="circle" style="display: none" id="circle"></div>
<div class="circle" id="circle-clone"></div>
<div class="rounded" id="rounded"></div>
<div class="square" id="square"></div>
</section>
<section>
<h4>Opções</h4>
<button style="display: none">Atualizar Página</button>
<button id='refresh-page' onclick="AtualizarPagina()">Atualizar Página</button>
<button id="show-element">Exibir Elementos Ocultos</button>
</section>
</body>
</html>
# Um axe representa um relacionamento com o nó atual e é usado para localizar nós relativos a esse nó na árvore.
//h4/following-sibling::div # Seleciona todos os irmãos após o nó atual
//button//preceding-sibling::h4 # Seleciona todos os irmãos antes do nó atual
//h4/ancestor::section # Seleciona todos os ancestrais (pai, avô, etc.) do nó atual
# 4 Localizadores
Localizadores XPATH - Outros exemplos
# Pegar texto dos elementos filhos
//body//child::*
# Pegar texto do nó principal sem os filhos
//body/text()
# Pegar textos começando com um valor
//body[starts-with(.,'Resultado')]
# Funcao text() not working (pegando o texto do nó pai)
//body/text()
# 4 Localizadores
Prática de Localizadores
- Crie o registro de uma pessoa
- Crie um XPATH ou CSS para acessar o botão de edição de acordo com o nome ou email da pessoa
- Edite o Nome ou Email da pessoa
- Printe o resultado alterado de acordo com o card exibido na tela
# 4 Localizadores
Shadow DOM
Um aspecto importante dos componentes da Web é o encapsulamento — ser capaz de manter a estrutura, o estilo e o comportamento da marcação ocultos e separados de outros códigos na página para que as diferentes partes não entrem em conflito e o código possa ser mantido limpo e organizado.
# 4 Localizadores
Shadow DOM - Acesso
O mecanismos de localização por CSS e Text acessam o Shadow DOM por padrão, seguindo os seguintes passos:
- Procuram os elementos no DOM na ordem de iteração
- Pesquisam recursivamente dentro do Shadow DOM abertos na ordem de iteração.
# 4 Localizadores
Shadow DOM - Ignorando acesso
page.locator(":light(localizador)")
Existe a possibilidade de ignorar o acesso ao Shadow DOM, para isso:
# 4 Localizadores
Prática Acessando Shadow DOM
- Implemente o comando para pegar o texto contido no placeholder do input dentro do Shadow DOM
# 4 Localizadores
Localizadores - Layout
:right-of(inner > selector) #Retorna elementos que estão a direita de acordo com o seletor
:left-of(inner > selector) #Retorna elementos que estão a esquerda de acordo com o seletor
:above(inner > selector) #Retorna elementos que estão acima de acordo com o seletor
:below(inner > selector) #Retorna elementos que estão abaixo de acordo com o seletor
:near(inner > selector) #Retorna elementos que estão próximos (dentro de 50 pixels CSS)
Os seletores de layout dependem do layout da página e podem produzir resultados inesperados. Por exemplo, um elemento diferente pode ser correspondido quando o layout muda em um pixel.
# 4 Localizadores
Prática Localizador Layout
- Implemente um localizador para identificar os elementos à direita do botão com texto 'Tab 2' e em seguida retorne o texto do primeiro elemento.
- Faça o mesmo para a esquerda.
- Implemente um localizador para retornar a quantidade de elementos acima e abaixo do botão com texto 'Tab 2'
- Implemente um localizador para retornar todos "inner html" dos elementos ao redor do botão com texto 'Tab 2'
# 4 Localizadores
Localizador Por Índice (nth)
Você pode restringir a consulta à n-ésima correspondência usando o seletor nth ou utilizar nth-match
# nth índice inicia de 0
# Click no primeiro botão
page.locator("button >> nth=0").click()
# Click último botão
page.locator("button >> nth=-1").click()
#:nth-match índice inicia de 1
# Preenche o segundo input da tela
page.locator(":nth-match(input, 2)").fill("xpto")
# 4 Localizadores
Prática com índice (nth)
- Implemente um localizador por posição para acessar o primeiro input com o atributo name 'num1'
- Implemente um localizador por posição para clicar no segundo elemento com texto 'Resultado'
# 4 Localizadores
Localizadores encadeados
Seletores podem ser combinados com a chave >>, por exemplo: 'seletor1 >> seletor2 >> seletor3'. Quando os seletores são encadeados, o próximo é consultado em relação ao resultado do anterior.
# Seleção encadeada
css=article >> css=.bar > .baz >> css=span[attr=value]
Equivale a:
document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]')
# Seleção com filtro de texto a partir do elemento raiz
css=article >> text=Hello
# 4 Localizadores
Outros Localizadores
O Playwright suporta atalhos para selecionar elementos usando certos atributos (não sendo o sugerido pela documentação). Atualmente, apenas os seguintes atributos são suportados:
- id
- data-testid
- data-test-id
- data-test
# Preenche um input com id "username"
page.locator('id=username').fill('value')
# Clica em um elemento com data-test-id "submit"
page.locator('data-test-id=submit').click()
# 4 Localizadores
Outros Localizadores
- Seletores de atributo não são seletores de CSS, então nada específico de CSS como :enabled não é suportado. Para mais recursos, use um seletor CSS adequado, por exemplo:
page.locator('data-test-id:light=submit').click()
css=[data-test="login"]:enable.
- Os seletores de atributo acessam o shadow DOM. Para desativar esse comportamento, use o sufixo :light após o atributo, por exemplo:
#5
Funções e Eventos
# 5 Funções e Eventos
Interação
# Clica no elemento identificado
page.locator(seletor).click()
# Input texto, data, time (limpando campo)
page.locator(seletor).fill("value")
# Combobox de Seleção
page.select_option(seletor, value='mul')
page.locator(seletor).select_option(value='blue')
page.locator(seletor).select_option(label='/')
page.locator(seletor).select_option(index=2)
Toda interação ocorrerá após a identificação do elemento (localizador), as principais que necessitamos são:
# 5 Funções e Eventos
Interação
# Digita caracter por caracter (na posição do cursor sem limpar)
page.locator('#area').type('Hello World!')
# Exemplo digitando uma letra a cada segundo
page.locator('#area').type('Hello World!', delay=1000)
Type - este método emitirá todos os eventos de teclado necessários, com todos os eventos keydown, keyup,
keypress no lugar. Você pode até especificar o atraso opcional entre as teclas pressionadas para
simular o comportamento real do usuário.
# 5 Funções e Eventos
Prática de Interação
- Implemente utilizando os comandos de interação os seguintes cálculos pela aplicação:
- Multiplicação de 2 x 4.
- Implemente a raiz quadrada de 4
- Implemente o coseno de 5
# 5 Funções e Eventos
Capturando conteúdo
# Retorna o texto do innerHtml do elemento encontrado pelo seletor
page.locator(seletor).inner_text()
''' Retorna o texto do innerHtml do primeiro elemento encontrado pelo
seletor (caso venha uma lista)'''
page.locator(seletor).first.inner_text()
# Retorna o texto do value do input encontrado pelo seletor
page.locator(seletor).input_value()
# Retorna a lista de innerHtml dos textos dos elementos encontrados pelo seletor
page.locator(seletor).all_inner_texts().__str__()
# Retorna o título da página
page.title()
# Quantidade de elementos retornados pelo seletor
page.locator(seletor).count()
# Retorna o valor de um atributo de acordo com o seletor
page.locator(seletor).get_attribute("placeholder")
Toda captura ocorrerá após a identificação do elemento (localizador), as principais que necessitamos são:
# 5 Funções e Eventos
Prática de Captura
- Implemente a captura do resultado dos cálculos.
- Implemente uma solução para coletar apenas a linha (marcada na imagem abaixo) que exibe o resultado do cálculo.
# 5 Funções e Eventos
Execução JavaScript
# Exemplo utilizando o objeto 'location'para obter informações do endereço da página atual
page.evaluate("window.location")
# Buscando o valor do input do primeiro input da tela
page.evaluate("document.getElementsByTagName('input')[0].value")
#Se o resultado for uma promessa ou se a função for assíncrona,
#a avaliação aguardará automaticamente até que seja resolvida:
status = page.evaluate("""async () => {
response = await fetch(location.href)
return response.status
}""")
# Exemplo utilizando argumento
page.evaluate('num => num * 5', 2)
# Utilizando objeto
print(page.evaluate('object => object.foo[0]', {'foo': [1,2,3]}))
A API page.evaluate(expression, **kwargs) pode executar uma função JavaScript no contexto da página da web e trazer os resultados de volta ao ambiente Playwright. Exemplos como: window e document podem ser usados no evaluate.
# 5 Funções e Eventos
Execução JavaScript
page.evaluate(expression, **kwargs) recebem um único argumento opcional. Este argumento pode ser uma mistura de valores Serializable e instâncias JSHandle ou ElementHandle
Nota: Como qualquer elemento DOM na página também é um objeto JavaScript, qualquer ElementHandle também é um JSHandle.
- JSHandle referencia qualquer objeto JavaScript na página
- ElementHandle referencia elementos DOM na página, possui métodos extras que permitem realizar ações nos elementos e afirmar suas propriedades.
# 5 Funções e Eventos
Experimente executar o código abaixo:
page.goto("http://vanilton.net/web-test/promise/")
# Cria o objeto window
object_handle = page.evaluate_handle('window')
print(object_handle.json_value())
# Passando por parâmetro um handle e recuperando seu innerHTML
body_handle = page.evaluate_handle('document.body')
body_text = page.evaluate("body => body.innerHTML", body_handle)
print(body_text)
# Encerra a referência ao elemento
body_handle.dispose()
object_handle.dispose()
Implemente o retorno da largura da tela pelo objeto screen dentro de window
Prática - JS Handle
# 5 Funções e Eventos
Execução JavaScript
ElementHandle representa um elemento DOM na página. ElementHandles podem ser criados com o método page.query_selector(selector, **kwargs).
Nota: O uso de ElementHandle é desencorajado, use objetos Locator e asserções web-first.
Handle aponta para um elemento DOM específico na página. Se esse elemento altera o texto ou é usado para renderizar um componente totalmente diferente, handle ainda está apontando para esse mesmo elemento DOM. Isso pode levar a comportamentos inesperados.
handle = page.query_selector("text=Submit")
handle.hover()
handle.click()
# 5 Funções e Eventos
Prática - ElementHandle
Experimente executar o código abaixo:
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.goto("http://vanilton.net/blog")
href_element = page.query_selector("a")
print(href_element.inner_text())
# 5 Funções e Eventos
Keyboard
# Realizar a ação diretamente no teclado virtual (onde estiver o foco)
page.keyboard.press('Meta+a', delay=1000)
# Realizar o Enter do teclado no seletor
page.locator('#submit').press('Enter')
# Realizar o atalho Control + seta para direita do teclado
page.locator('#name').press('Control+ArrowRight')
# Digitar uma tecla $ com o teclado
page.locator('#value').press('$')
# Selecionando o valor do input (Meta no MacOs) com delay de 1s
page.locator('input >> nth=0').press('Meta+a', delay=1000)
Keyboard fornece uma API para gerenciar um teclado virtual. A API de alto nível é keyboard.type(texto, **kwargs), que recebe caracteres brutos e gera eventos keydown, keypress/input e keyup adequados em sua página.
# 5 Funções e Eventos
Keyboard
-
F1-F12
-
Digit0-Digit9
-
KeyA-KeyZ
-
Backquote
-
Minus
-
Equal
-
Backslash
-
Backspace
-
Tab
-
Delete
-
Escape
-
ArrowDown
-
End
-
Enter
-
Home
-
Insert
-
PageDown e
PageUp
-
ArrowRight
-
ArrowUp
, etc.
Outros exemplos de Keys
-
Shift
-
Control
-
Alt
-
Meta
-
ShiftLeft.
Atalhos de Modificação
# 5 Funções e Eventos
Keyboard
page.keyboard.type("Hello World!")
page.keyboard.press("ArrowLeft")
page.keyboard.down("Shift") # down (segura a tecla)
for i in range(6):
page.keyboard.press("ArrowLeft")
page.keyboard.up("Shift") # up (solta a tecla)
page.keyboard.press("Backspace")
Fazendo a inserção desses comandos ao final, qual o valor que estará contido no input?
# 5 Funções e Eventos
Keyboard
Acesse: https://keycode.info
- Vamos simular algumas teclas e obter o resultado do card 'Event Dump'
- Tirar um Screenshot da tela e salve em uma pasta com nome 'image'
# 5 Funções e Eventos
Eventos
page = sync_playwright().start().chromium.launch(headless=False).new_page()
#with page.expect_request("**/*teste*.png") as first:
with page.expect_request("**teste_agil-520x389.png") as first:
page.goto("http://vanilton.net/blog")
print(first.is_done())
print(first.value.headers)
print(first.value.url)
print(first.value.response().status)
O Playwright permite ouvir vários tipos de eventos que acontecem na página da web, como solicitações de rede, criação de páginas filhas, trabalhadores dedicados etc. Existem várias maneiras de se inscrever nesses eventos:
# 5 Funções e Eventos
Eventos
def print_request_sent(request):
print("Request sent: " + request.url)
def print_request_finished(request):
print("Request finished: " + request.url)
def print_request_response(request):
print("Request response: " + request.url)
# Execute essa função e observe o console da IDE
def test_wait_add_remove_event_listener():
page = sync_playwright().start().chromium.launch(headless=False).new_page()
page.on("request", print_request_sent)
page.on("response", print_request_response)
page.on("requestfinished", print_request_finished)
#page.remove_listener("requestfinished", print_request_finished)
page.goto("https://ge.globo.com")
page.goto("https://wikipedia.org")
Vamos verificar como acompanhar requests realizadas no navegador, para isso execute o exemplo abaixo:
# 5 Funções e Eventos
Frames
page.goto("http://vanilton.net/web-test/iframe/")
frame_element = page.frame_locator('iframe')
frame_element.locator('text=Produtos').click()
print(frame_element.locator('css=title').inner_text())
Uma página pode ter um ou mais objetos Frame anexados a ela. Cada página tem um quadro principal e as interações no nível da página (como clique) devem operar no quadro principal.
Implemente escolher um dos posts que será carregado no iframe e exiba o título da página.
# 5 Funções e Eventos
Checkboxes e RadioButtons
# Marca o checkbox
page.locator('#agree').check()
# Verifica o estado de marcado
assert page.locator('#agree').is_checked() is True
# Desmarca um checkbox marcado <label>.
page.locator('#subscribe-label').uncheck()
# Seleciona um elemento radio button
page.locator('text=XL').check()
Essa é a maneira mais fácil de marcar e desmarcar uma caixa de seleção ou um botão de opção. Este método pode ser usado com input[type=checkbox], input[type=radio], [role=checkbox] ou rótulo associado com checkbox ou botão de rádio.
# 5 Funções e Eventos
Checkboxes e RadioButtons
# Identificar o checkbox ou radio contendo XL
element = page.get_by_label('XL')
# Verifica o estado de marcado
assert element.is_checked() is True
# Desmarca um checkbox marcado <label>.
element.uncheck()
# Marcar o elemento
element.check()
Utilizando o localizador padrão get_by_label
Caso existam label com valores que conflitem e deseje identificar o valor exato utilize:
page.get_by_label("parametro", exact=True)
# 5 Funções e Eventos
Prática
- Selecione um radio button e verifique o resultado na div com id 'output'
- Marque os radios buttons e verifique a saída na div com id 'output-check'
- Verifique o status de elementos marcados
# 5 Funções e Eventos
Screenshot
# Print da tela de acordo com o tamanho da tela
page.screenshot(path='caminho_da_imagem')
# Print de toda a tela (fullscreen)
page.screenshot(path='caminho_da_imagem', full_page=True)
# Base64 da imagem completa
# Experimente colar a string gerada em: https://codebeautify.org/base64-to-image-converter
screenshot_bytes = page.screenshot(full_page=True)
print(base64.b64encode(screenshot_bytes).decode())
# Print de um elemento específico
page.locator('#xpto').screenshot(path='caminho/imagem.png')
A API de capturas de tela aceita muitos parâmetros para formato de imagem, área de clipe, qualidade, etc. Verifique-os.
# 5 Funções e Eventos
Prática
Acesse: http://lojafake.vanilton.net
- Crie uma função para capturar todas as imagens dos elementos <img> da loja
- Salve as imagens numa pasta chamada 'images'
- O nome das imagens deve ser de acordo com o valor do atributo 'name'
# 5 Funções e Eventos
Highlight
# Marca os elementos identificados
page.locator("input").highlight()
Realce o(s) elemento(s) correspondente(s) na tela.
Acesse: https://vanilton.net/web-test/calculatorSoapPHP/ e teste com o código abaixo:
Útil para depuração.
# 5 Funções e Eventos
Prática
-
Implemente o login no site http://lojafake.vanilton.net/
- Usuário: admin
- Senha: 1234
- Acesse o menu 'Minhas Listas'
- Acesse a Aba Filmes
to be continue...
# 5 Funções e Eventos
Drag and Drop
Arrastando e soltando elementos
page.drag_and_drop(source, target, **kwargs)
page.goto("http://vanilton.net/web-test/drag-drop/")
page.drag_and_drop("id=drag1", 'id=div2')
print(page.locator("[id='content1']").inner_text())
page.drag_and_drop("id=drag1", "id=div1")
print(page.locator("[id='content2']").inner_text())
Funciona utilizando a api HTML 5 Nativa
# 5 Funções e Eventos
Drag and Drop
Continuando o exercício da lojafake, agora experimentem realizar o drag drop de um filme para a área de assistidos no site da loja utilizando a função drag and drop.
# 5 Funções e Eventos
Drag and Drop
Podemos realizar o arraste e soltar utilizando a função move e hover e com apoio do bouding_box()
page.hover()
page.mouse.down()
page.locator().bounding_box()
page.mouse.up()
Explorem os comportamentos das funções e implemente o arraste de um filme para a área de Filmes Vistos
# 5 Funções e Eventos
Trace
# Inicia o rastreamento antes de criar ou navegar em páginas
context.tracing.start(screenshots=True, snapshots=True, sources=True)
page.goto("https://playwright.dev")
# Encerra o rastreamento e exporta em .zip
context.tracing.stop(path = "trace.zip")
O Trace Viewer é uma ferramenta GUI que ajuda a explorar os passos gravados após a execução do script.
Métodos para visualização do trace
- playwright show-trace trace.zip
- trace.playwright.dev
Local
Nuvem
# 5 Funções e Eventos
Prática
- Adicione o trace para rastrear os passos do último exercício.
- Envie o trace.zip para um dos seus colegas visualizar o resultado.
# 5 Funções e Eventos
Configurações
#timeout em milissegundos
# Essa configuração alterará o tempo máximo de navegação padrão para os seguintes métodos e atalhos relacionados:
page.set_default_navigation_timeout(timeout)
# Essa configuração alterará o tempo máximo padrão para todos os métodos que aceitam a opção de timeout.
page.set_default_timeout(timeout)
# Alterar o tamanho da página (também pode ser utilizado no contexto)
page.set_viewport_size({"width": 640, "height": 480})
# 5 Funções e Eventos
Esperas automáticas
O Playwright realiza uma série de verificações de ação nos elementos antes de fazer ações para garantir que essas ações se comportem conforme o esperado. Ele espera automaticamente que todas as verificações relevantes sejam aprovadas e só então executa a ação solicitada. Se as verificações necessárias não passarem dentro do tempo limite determinado, a ação falhará com TimeoutError.
# 5 Funções e Eventos
Esperas automáticas
Action | Attached | Visible | Stable | Receives Events | Enabled | Editable |
---|---|---|---|---|---|---|
check | Yes | Yes | Yes | Yes | Yes | - |
click | Yes | Yes | Yes | Yes | Yes | - |
dblclick | Yes | Yes | Yes | Yes | Yes | - |
setChecked | Yes | Yes | Yes | Yes | Yes | - |
tap | Yes | Yes | Yes | Yes | Yes | - |
uncheck | Yes | Yes | Yes | Yes | Yes | - |
hover | Yes | Yes | Yes | Yes | - | - |
scrollIntoViewIfNeeded | Yes | - | Yes | - | - | - |
screenshot | Yes | Yes | Yes | - | - | - |
fill | Yes | Yes | - | - | Yes | Yes |
selectText | Yes | Yes | - | - | - | - |
dispatchEvent | Yes | - | - | - | - | - |
focus | Yes | - | - | - | - | - |
getAttribute | Yes | - | - | - | - | - |
innerText | Yes | - | - | - | - | - |
innerHTML | Yes | - | - | - | - | - |
press | Yes | - | - | - | - | - |
setInputFiles | Yes | - | - | - | - | - |
selectOption | Yes | Yes | - | - | Yes | - |
textContent | Yes | - | - | - | - | - |
type | Yes | - | - | - | - | - |
O elemento é considerado anexado quando conectado a um Documento ou a um ShadowRoot.
visibility:hidden
display:none
O elemento é considerado estável quando manteve a mesma caixa delimitadora por pelo menos dois quadros de animação consecutivos.
O elemento é considerado habilitado a menos que seja um <button>, <select>, <input> ou <textarea> com uma propriedade desabilitada.
O elemento é considerado editável quando habilitado e não possui conjunto de propriedades readonly.
O elemento é considerado receptor de eventos de ponteiro quando é o alvo de acerto do evento de ponteiro no ponto de ação.
# 5 Funções e Eventos
Espera Implícitas
browser = sync_playwright().start().chromium.launch(timeout=60000)
# 5 Funções e Eventos
Espera Explícitas
page.wait_for_timeout(timeout)
time.sleep(5)
page.wait_for_url(url, **kwargs)
page.wait_for_selector(selector, **kwargs)
page.wait_for_load_state(**kwargs)
page.wait_for_event(event, **kwargs)
# 5 Funções e Eventos
Prática
- Implemente um teste para a funcionalidade de cadastro de um usuário verificando sua inclusão na tabela
- Implemente a remoção de um usuário, verificando sua exclusão da tabela.
# 5 Funções e Eventos
- Browser Contexts fornecem uma maneira de operar várias sessões de navegador independentes.
- O Playwright permite criar contextos de navegador "anônimos" com o método browser.new_context(**kwargs). Os contextos de navegador "anônimos" não gravam nenhum dado de navegação no disco.
Browser Contexts
# 5 Funções e Eventos
Nunca reiniciar o Browser
- Instanciação Lenta
- Grande sobrecarga de memória
Browser Contexts
Sempre cria uma instância do Browser
- Completo isolamento
- Rápida instanciação
- Baixa sobrecarga de memória
Browser Context
# 5 Funções e Eventos
Browser Contexts
# 3 Conhecendo o Playwright
Browser Contexts
Browser
Context
Context
Page
Page
Page
Page
browser.close()
context.close()
page.close()
Anônimo
Anônimo
# 5 Funções e Eventos
Context outras propriedades
browser = sync_playwright().start().chromium.launch(headless=False)
context = browser.new_context(
geolocation={'longitude': 12.492348, 'latitude': 41.890221},
permissions=['geolocation'],
record_video_dir='video/',
record_video_size={'width': 800, 'height': 600},
timezone_id="Europe/Rome",
locale="it-IT"
)
page = context.new_page()
page.goto("https://maps.google.com")
page.locator("button[aria-label='Mostra la tua posizione']").click()
# 5 Funções e Eventos
Prática
- Crie um script para carregar o contexto com o timezone de Nova York, locale da Inglaterra
- Acesse sua localização
- Retire um print da tela.
# 5 Funções e Eventos
Upload
# Select one file
page.locator('input#upload').set_input_files('myfile.pdf')
# Select multiple files
page.locator('input#upload').set_input_files(['file1.txt', 'file2.txt'])
# Remove all the selected files
page.locator('input#upload').set_input_files([])
# Upload buffer from memory
page.locator("input#upload").set_input_files(
files=[
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
],
)
Você pode selecionar arquivos de entrada para upload usando o método page.set_input_files(selector, files, **kwargs)
# 5 Funções e Eventos
Prática
- Implemente o upload de uma imagem
- Verifique a url gerada pelo nome do arquivo.
- Exiba em um print() a url da imagem.
- Crie um método que verifique a regra de arquivo maior que 500KB
# 5 Funções e Eventos
Prática
- Implemente o upload de 2 ou mais arquivos
- Verifique se os mesmos serão exibido na listagem
- Limpe a lista de upload
- Verifique a mensagem 'Nenhum arquivo selecionado'
#6
Inspetor, Gerador de Teste e Relatórios
# 6 Inspetor, Gerador de Teste e Relatório
Gerador de código
playwright codegen lojafake.vanilton.net
# 6 Inspetor, Gerador de Teste e Relatório
Gerador de código
playwright codegen --save-storage=auth.json
Por vezes é necessário capturar tokens ou outros valores em storage do navegador que podem ser reaproveitados em outros testes, para isso podemos executar o comando abaixo:
Experimente acessar https://vanilton.net/web-test/crud-local-storage/ cadastrar duas pessoas e fechar o navegador.
# 6 Inspetor, Gerador de Teste e Relatório
Restaurando storage
playwright open --load-storage=auth.json https://vanilton.net/web-test/crud-local-storage/
Agora vamos restaurar o storage salvo no último exercício
Importante saber que são restaurados apenas dados de localStorage e cookies
# 6 Inspetor, Gerador de Teste e Relatório
Gerando código com configuração personalizada
page.pause()
Executando a função pause contido no objeto page será chamado o inspetor de geração de código e será pausado o seu script, podendo assim iniciar uma gravação e reproduzir novos passos para geração de código.
# 6 Inspetor, Gerador de Teste e Relatório
Gerando código com configuração personalizada
# Retorna um elemento
playwright.$('body')
# Retorna todas incidências
playwright.$$('body')
# Retorna a localização elemento com o texto
playwright.inspect('text=Log in')
# Procurando um link contendo um texto
playwright.locator('a', { hasText: 'O que é' });
# Procuranto por localizadores padrões
playwright.getByRole('button', 'name=Aceitar')
Depois de definir um ponto de interrupção em seu teste, você poderá executá-lo com PWDEBUG=console.
# 6 Inspetor, Gerador de Teste e Relatório
Gerando código mais opções
playwright codegen https://vanilton.net -o meu_script.py --target python --save-trace trace_vanilton.zip
É possível gerar o código gravado e salvá-lo em um script de saída pré-determinado, definindo também a linguagem que será gerado o código fonte e por fim incluir o rastreio de todo o teste por meio do save trace.
Atualizado em: 29/06/2023
# 6 Inspetor, Gerador de Teste e Relatório
Relatório
# Vamos criar um script runner para criar o report das nossas classes de teste unittest
import unittest
from HTMLTestRunner.runner import HTMLTestRunner
from pythonModule import ClassTest
test1 = unittest.TestLoader().loadTestsFromTestCase(ClassTest1)
test2 = unittest.TestLoader().loadTestsFromTestCase(ClassTest2)
suite = unittest.TestSuite([test1, test2])
runner = HTMLTestRunner(log=True, verbosity=2, output='report', title='Test report', report_name='report',
open_in_browser=True, description="Testes da Aplicação XPTO", tested_by="Vanilton Pinheiro",
add_traceback=True, style=style)
runner.run(suite)
pip install HTMLTestRunner-rv
# 6 Inspetor, Gerador de Teste e Relatório
Relatório
#Plawright
Avançado
1
Revisão de conceitos básicos de Playwright
2
Testes de fluxo de navegação complexos
3
Estruturação de testes reutilizáveis
5
Geração de relatórios de teste avançados
4
Integração com sistemas de build e CI/CD
6
Otimização de testes para desempenho e velocidade
# O que veremos no Avançado
# Avançado 1 - Revisão de conceitos básicos de Playwright
Conceitos do Playwright
# Avançado 1 - Revisão de conceitos básicos de Playwright
Playwright - Arquitetura
# Avançado 1 - Revisão de conceitos básicos de Playwright
Arquitetura do Playwright
# Avançado 1 - Revisão de conceitos básicos de Playwright
Arquitetura do Playwright
Teste
HTTP Request
Web Socket (TCP)
Abrir o navegador
Clicar no elemento
Comandos
# Avançado 1 - Revisão de conceitos básicos de Playwright
Criando o projeto Python com Pycharm
# Avançado 1 - Revisão de conceitos básicos de Playwright
Instalando Playwright com Pytest
# Instalando com plugin Pytest
pip install pytest-playwright
# Atualizando versão instalada para mais atual
pip install -U pytest-playwright
# Instalando navegadores
playwright install
# Avançado 1 - Revisão de conceitos básicos de Playwright
Aqui o modo de instalação sem o Plugin Pytest
Desinstalando Playwright com Pytest
Nota: Desinstalação Incluída na versão 1.35.0
# Remove os navegadores instalados por esta instalação
playwright uninstall
# Remova todos os navegadores Playwright já instalados
playwright uninstall --all
# Avançado 1 - Revisão de conceitos básicos de Playwright
Acompanhando atualizações
Nota: Desinstalação Incluída na versão 1.35.0
# Avançado 1 - Revisão de conceitos básicos de Playwright
O Playwright está em constante atualização, para verificar o que há de novo acesse: https://playwright.dev/docs/release-notes
Ou se desejar acesse o github oficial (repositório da versão cliente em python) https://github.com/microsoft/playwright-python
Interactive mode ou REPL -
Read-Eval-Print Loop
>>> from playwright.sync_api import sync_playwright
>>> playwright = sync_playwright().start()
>>> browser = playwright.chromium.launch(headless=False)
>>> page = browser.new_page()
>>> page.goto("https://vanilton.net")
>>> site_title = page.title()
>>> page.screenshot(path=f'{site_title}.png')
>>> browser.close()
>>> playwright.stop()
Vamos rever as principais funcionalidade Playwright para executar seus testes, para isso podemos utilizar o modo interativo, logo:
1. Abra o terminal python e em seguida:
# Avançado 1 - Revisão de conceitos básicos de Playwright
Revisando Localizadores
# Avançado 1 - Revisão de conceitos básicos de Playwright
- Como já conseguimos validar utilizando o modo interativo, podemos então criar nosso primeiro script no PyCharm.
- Experimente executar o código do modo interativo via script na IDE
Revisando Localizadores
# Avançado 1 - Revisão de conceitos básicos de Playwright
# Avançado 1 - Revisão de conceitos básicos de Playwright
Expectativas
Realidade
Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
As expectativas são as verificações realizadas pelo Playwright em torno do que é retornado (realidade) pelo software em teste, logo um resultado positivo ou negativo para a expectativa.
Prática Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
- Vamos acessar a aplicação todos, que nada mais é que uma lista de atividades a serem feitas com o status completado ou ativas. A aplicação permite além de inserir tarefas obviamente, realizar remoção e edição das tarefas com clique duplo.
- Acesse: https://vanilton.net/web-test/todos/
- Insira 3 tarefas
- Complete uma das tarefas adicionadas
- Acesse a lista de tarefas completadas
- Verifique a tarefa na lista através da expect:
from playwright.sync_api import sync_playwright, expect
..
..
expect(locator).to_be_visible()
Prática Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
As expectativas possuem um valor de espera padrão definido em 5 segundos, para alterar temos duas opções:
# 1 - Definindo globalmente o tempo de espera para o objeto expect
from playwright.sync_api import sync_playwright, expect
expect.set_options(timeout=10_000)
# 2 - Definindo no uso individual da expect
expect(page.get_by_text("Name")).to_be_visible(timeout=10_000)
- Experimente o uso pelo global e o individual alterando o localizador e forçando sua falha, e consequentemente gerando um timeout.
Prática Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
- Acesse: https://vanilton.net/web-test/todos/
- Insira 3 tarefas
- Complete uma das tarefas adicionadas
- Acesse a lista de tarefas Ativas (Active)
- Verifique a não presença da lista de tarefas completas
from playwright.sync_api import sync_playwright, expect
..
..
expect(locator).to_be_hidden()
Prática Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
Ainda na aplicação todos faça:
- Acesse a lista de tarefas Completed
- Verifique o texto "completed" na url
from playwright.sync_api import sync_playwright, expect
import re
..
..
expect(page).to_have_url(re.compile(".*texto"))
Praticando Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
- Agora vamos verificar a quantidade de tarefas listadas em All e Completed:
from playwright.sync_api import sync_playwright, expect
..
..
expect(locator).to_have_count(count)
Praticando Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
Agora vamos verificar uma imagem pelo seu atributo src e id, para isso:
- Acesse https://vanilton.net/web-test/triangulo_v2/
- Faça o fluxo de um triângulo equilátero.
- Verifique o atributo src e id da imagem utilizando expect, conforme exemplos abaixo.
from playwright.sync_api import sync_playwright, expect
..
..
expect(locator).to_have_attribute("src", "equilatero.png")
expect(locator).to_have_id("seu_id")
Praticando Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
Agora vamos verificar campos editáveis, vazio, com e sem foco, possuindo um determinado css, estando vazio ou preenchido e habilitado para clique.
- Acesse https://vanilton.net/web-test/textarea/
- Implemente o fluxo na aplicação que utilize todos as expectativas abaixo, perceba que o botão alterar o estado do textarea e o checkbox altera o estado do botão.
from playwright.sync_api import sync_playwright, expect
..
..
locator = page.get_by_role("textbox")
expect(locator).to_be_editable()
expect(locator).not_to_be_editable()
expect(locator).to_be_focused()
expect(locator).not_to_be_focused()
expect(locator).to_have_css("display", "flex")
expect(locator).not_to_have_css(name, value)
expect(locator).to_be_empty()
expect(locator).not_to_be_empty()
expect(locator).to_be_enabled()
expect(locator).not_to_be_enabled()
Para mudar o padrão de ID utilizado:
playwright.selectors.set_test_id_attribute("seu_id")
Praticando Expectativas
# Avançado 1 - Revisão de conceitos básicos de Playwright
Também é possível realizar várias concatenações de checagem por meio do and_ ou or_ sempre direcionado ao mesmo elemento buscado.
- Acesse https://vanilton.net/web-test/textarea/
- Experimento o código abaixo
expect(page.get_by_test_id("story").and_(page.locator("[name='story']"))).to_be_visible()
expect(page.get_by_test_id("story").or_(page.locator("[name='story2']"))).to_be_visible()
#2 Avançado
Testes de fluxo de navegação complexos
Aplicação de teste
# Avançado 2 - Fluxo de teste complexo
É uma plataforma de comércio eletrônico de código aberto.
Saleor é o e-commerce nº 1 no GitHub e a estratégia mais preparada para o futuro para qualquer tecnologia de visualização de marca, extensibilidade e abertura como vitais para experiências revolucionárias do cliente.
Aplicação de teste - Conhecendo e praticando
- Na IDE PyCharm crie um Python Package com nome "tests"
- Neste pacote agora crie um script python "test_saleor.py"
- Acesse a página abaixo no seu navegador:
# Avançado 2 - Fluxo de teste complexo
Aplicação de teste - Conhecendo e praticando
- Agora vamos autenticar:
-
Email address: admin@example.com
-
Password: admin
-
-
Após autenticar crie as seguintes expects :
-
Deve estar visível mensagem de boas vindas "Hello there, admin@example.com".
-
Verificar a presença dos menus "Produtos", "Orders", "Discounts" com os textos informados e ignorando case sensitive, utilize expect(locator).to_have_text(..)
- Verifique a presença de todos os menus e submenus, utilize expect(locator).to_contain_text()
- Verifique a quantidade de itens no menu por meio do expect().to_have_count(numero_esperado)
-
# Avançado 2 - Fluxo de teste complexo
Aplicação de teste - Conhecendo e praticando
-
Criar um localizador para identificar a lista de atividades
-
Deve ser possível iterar nos itens da lista
-
Criar uma lista de Ordens extraída da lista de atividades, considerar apenas o números das ordens, pode-se utilizar regex
-
Abrir uma página nova e na tela de pesquisa de ordens, consultar os números das ordens contidas na lista
-
Por fim somar todos os valores das ordens pesquisadas
# Avançado 2 - Fluxo de teste complexo
Extra: Utilize o context.storage_state para salvar o storage do login e utilizá-lo posteriormente.
# Avançado 2 - Fluxo de teste complexo
Dicionário de dados - Shipping Zone
Shipping Zone | Descrição |
---|---|
Envio Nacional | Essa opção inclui todas as regiões dentro do país em que a empresa está localizada. Por exemplo, se a empresa está nos Estados Unidos, seria o envio para todos os estados dentro dos EUA. |
Envio Internacional | Essa opção abrange todos os países para os quais a empresa está disposta a enviar seus produtos. Pode ser dividida em regiões geográficas, como América do Norte, Europa, Ásia, etc. |
Envio Regional | Nessa opção, a empresa pode definir zonas de envio com base em áreas geográficas específicas dentro de um país. Por exemplo, regiões, estados ou províncias específicos. |
Envio por Continente | Essa opção agrupa os países por continente. Por exemplo, América do Norte, América do Sul, Europa, África, Ásia, Oceania. |
Envio Local | Essa opção é adequada para empresas que têm uma loja física ou depósito em uma determinada área e desejam oferecer opções de retirada na loja ou entrega local. |
Envio Internacional Selecionado: | Nessa opção, a empresa pode selecionar manualmente os países ou regiões específicos para os quais está disposta a enviar, em vez de incluir todos os países. |
Envio Global | Essa opção é adequada para empresas que desejam oferecer envio para qualquer país ao redor do mundo, sem restrições. |
# Avançado 2 - Fluxo de teste complexo
Dicionário de dados - Channel (Canais de Venda)
Canal de venda | Descrição |
---|---|
Lojas físicas | Essas são as lojas tradicionais, onde os clientes podem visitar pessoalmente, ver e tocar os produtos antes de fazer uma compra. Podem ser lojas de varejo, shoppings, boutiques, supermercados, entre outros. |
Comércio eletrônico (e-commerce) | Os canais de venda online têm se tornado cada vez mais populares. As lojas virtuais permitem que os clientes façam compras pela internet, escolhendo produtos, adicionando ao carrinho e efetuando o pagamento online. Exemplos incluem sites de varejistas, marketplaces, como Amazon e eBay, e lojas próprias de marcas. |
Marketplaces | São plataformas que reúnem diversos vendedores em um único local, proporcionando aos consumidores uma ampla variedade de produtos de diferentes marcas e categorias. Alguns exemplos famosos são o Amazon Marketplace, o Mercado Livre e o Etsy. |
Vendas diretas | Esse modelo envolve a venda de produtos diretamente aos consumidores, sem intermediários. Pode ser realizado através de representantes de vendas independentes (consultores, revendedores), que fazem demonstrações e vendas em domicílio, por exemplo. |
Vendas por telefone | Algumas empresas têm serviços de atendimento ao cliente por telefone, onde os clientes podem fazer pedidos diretamente com um representante de vendas. |
Redes sociais | As redes sociais têm se tornado um canal de vendas popular, permitindo que as empresas exibam seus produtos, interajam com os clientes e efetuem vendas diretamente através dessas plataformas, como Facebook, Instagram, Pinterest e TikTok. |
Canais de TV | Alguns produtos são vendidos através de programas de televendas, onde os apresentadores demonstram e promovem os produtos ao vivo e os telespectadores podem fazer pedidos por telefone ou online. |
Venda por catálogo | Esse modelo envolve o uso de catálogos impressos ou digitais, onde os clientes podem ver os produtos, fazer pedidos e aguardar a entrega. |
# Avançado 2 - Fluxo de teste complexo
Dicionário de dados - Produtos
Tipo de Produto | Categoria | Coleções | Categorias:Produtos |
---|---|---|---|
Eletrônicos | Smartphones Laptops TVs Câmeras digitais Fones de ouvido |
Coleção "Smart Tech": incluindo smartphones, smartwatches e dispositivos domésticos inteligentes. Coleção "Gaming Gear": com laptops, teclados, mouses e fones de ouvido voltados para jogos. Coleção "Home Entertainment": com TVs, alto-falantes e sistemas de som para uma experiência de entretenimento em casa aprimorada. |
Smartphones: iPhone 13, Samsung Galaxy S21, Google Pixel 6. Laptops: MacBook Pro, Dell XPS 15, Lenovo ThinkPad. TVs: LG OLED CX, Sony Bravia A80J, Samsung QN90A. Câmeras digitais: Canon EOS R5, Sony Alpha A7 III, Nikon Z7. Fones de ouvido: Apple AirPods Pro, Sony WH-1000XM4, Jabra Elite 85t. |
Vestuário e Moda | Camisetas Calças Vestidos Sapatos Acessórios (bolsas, cintos, chapéus) |
Coleção "Tendências de Verão": com roupas leves, estampas florais e cores vibrantes para a estação. Coleção "Roupas de Trabalho": focada em trajes elegantes e profissionais para o ambiente corporativo. Coleção "Streetwear Urbano": com peças modernas e despojadas, inspiradas na cultura das ruas. |
Camisetas: Nike Dri-FIT, Adidas Originals Trefoil, H&M Basic Tee. Calças: Levi's 501, Lululemon ABC Jogger, Zara Slim-Fit Chinos. Vestidos: Reformation midi dress, Zara floral sundress, ASOS wrap dress. Sapatos: Nike Air Max 90, Converse Chuck Taylor All Star, Doc Martens 1460. Acessórios (bolsas, cintos, chapéus): Louis Vuitton Speedy Bag, Gucci GG Belt, Brixton Fedora Hat. |
Alimentos | Frutas e vegetais frescos Carnes e aves Produtos lácteos Cereais e grãos Snacks e petiscos |
Coleção "Sabores Internacionais": oferecendo uma variedade de alimentos e ingredientes étnicos de diferentes partes do mundo. Coleção "Opções Saudáveis": com produtos orgânicos, vegetarianos, veganos e sem glúten para uma alimentação mais saudável. Coleção "Snacks Gourmet": trazendo petiscos exclusivos e gourmet, perfeitos para momentos especiais. |
Frutas e vegetais frescos: maçãs, bananas, cenouras. Carnes e aves: frango, carne bovina, peixe salmão. Produtos lácteos: leite, queijo cheddar, iogurte grego. Cereais e grãos: arroz integral, aveia em flocos, macarrão de trigo integral. Snacks e petiscos: batatas fritas, chocolate, castanhas. |
Móveis | Sofás Camas Mesas de jantar Armários Estantes |
Coleção "Estilo Minimalista": com móveis de linhas simples, cores neutras e designs clean. Coleção "Estilo Vintage": apresentando móveis retrô, peças antigas restauradas e acabamentos vintage. Coleção "Mobília Modular": oferecendo móveis versáteis e funcionais que se adaptam a diferentes espaços e necessidades. |
Sofás: sofá de couro, sofá-cama, sofá modular. Camas: cama king size, cama de plataforma, cama com gavetas de armazenamento. Mesas de jantar: mesa de jantar retangular, mesa de jantar redonda, mesa de jantar de madeira maciça. Armários: armário de quarto, armário de cozinha, armário de banheiro. Estantes: estante de madeira, estante de metal, estante modular. |
Beleza e Cuidados Pessoais | Maquiagem Perfumes Produtos para cuidados com a pele Produtos para cuidados com o cabelo Produtos de higiene pessoal |
Coleção "Cuidados com a Pele Natural": com produtos feitos com ingredientes naturais e orgânicos. Coleção "Maquiagem Glamourosa": com tons vibrantes, brilhos e produtos de alta qualidade para looks glamourosos. Coleção "Cuidados Masculinos": voltada para produtos de barbear, cuidados com a barba e cuidados com o cabelo masculino. |
Maquiagem: base líquida, batom matte, paleta de sombras. Perfumes: Chanel No. 5, Dior Sauvage, Jo Malone English Pear & Freesia. Produtos para cuidados com a pele: limpador facial, creme hidratante, máscara facial. Produtos para cuidados com o cabelo: shampoo, condicionador, óleo capilar. Produtos de higiene pessoal: sabonete líquido, desodorante, creme dental. |
Automóveis | Carros de passeio SUVs Motocicletas Caminhões Veículos elétricos |
Coleção "Carros Esportivos": apresentando modelos de alta performance, design arrojado e tecnologia avançada. Coleção "Carros Elétricos": com veículos elétricos e híbridos, focados na sustentabilidade e eficiência energética. Coleção "Aventura Todo-Terreno": incluindo veículos off-road, SUVs robustos e caminhões para aventuras off-road. |
Carros de passeio: Toyota Corolla, Honda Civic, Volkswagen Golf. SUVs: Ford Explorer, Nissan Rogue, Jeep Grand Cherokee. Motocicletas: Yamaha YZF-R6, Harley-Davidson Street Glide, Honda CBR500R. Caminhões: Ford F-150, Chevrolet Silverado, Ram 1500. Veículos elétricos: Tesla Model 3, Nissan Leaf, Chevrolet Bolt EV. |
Esportes e Lazer | Bicicletas Equipamentos de ginástica Artigos esportivos (bolas, tacos, raquetes) Jogos de tabuleiro Livros e filmes |
Coleção "Fitness em Casa": com equipamentos de ginástica compactos e acessórios para treinos em casa. Coleção "Esportes Aquáticos": oferecendo equipamentos para mergulho, surf, stand-up paddle e outros esportes aquáticos. Coleção "Clássicos dos Jogos": trazendo jogos de tabuleiro tradicionais, como xadrez, damas e gamão. |
Bicicletas: Mountain Bike: Trek Fuel EX, Specialized Stumpjumper, Giant Anthem. Equipamentos de ginástica: Esteira NordicTrack Commercial 1750, ProForm Pro 2000, Sole F80. Artigo esportivo: Bola de Futebol: Nike Premier League Strike, Adidas Tango, Select Numero 10. Jogos de tabuleiro: xadrez, damas, gamão, Carcassonne, Settlers of Catan Livros e filmes: Biografias de atletas famosos, livros sobre técnicas e estratégias esportivas. |
Casa e Decoração: | Utensílios de cozinha Decoração de parede Cortinas e persianas Tapetes e carpetes Iluminação |
Coleção "Estilo Escandinavo": com móveis e acessórios de decoração minimalistas, funcionais e aconchegantes. Coleção "Decoração Boho": apresentando elementos étnicos, cores vibrantes e texturas naturais para uma atmosfera boêmia. Coleção "Tecnologia Inteligente para Casa": com dispositivos domésticos conectados, como lâmpadas inteligentes, termostatos e sistemas de segurança. |
Utensílios de cozinha: Conjunto de panelas antiaderentes Decoração de parede: Relógios de parede elegantes Cortinas e persianas: Cortinas de linho, Persianas de madeira, Cortinas blackout Tapetes e carpetes: Tapete de lã felpudo, Tapete persa vintage, Tapete geométrico moderno Iluminação: Lustre de cristal, Luminária de teto embutida, Lâmpada de mesa com cúpula |
# Avançado 2 - Fluxo de teste complexo
Aplicação de teste - Instalação local
#Clone Saleor's repo
git clone https://github.com/saleor/saleor-platform.git --recursive --jobs 3
cd saleor-platform
docker-compose build
##Apply database migrations:
docker-compose run --rm api python3 manage.py migrate
docker-compose run --rm api python3 manage.py collectstatic --noinput
Optionally
# Finally, create yourself an admin account:
docker-compose run --rm api python3 manage.py createsuperuser
docker-compose up -d
- Pré-condição para instalação
- Git
- Docker
- Execute os passos abaixo
# Avançado 2 - Fluxo de teste complexo
Garanta que as portas abaixo estão livres:
1025
8025
8000
9000
5432
6379
5775 e 78
6831 e 32
14268
9411
Aplicação de teste - Instalação local
- Acesse http://localhost:9000
# Avançado 2 - Fluxo de teste complexo
Aplicação de teste - Equipes
# Avançado 2 - Fluxo de teste complexo
Repositório | Tipo de Produto | Panda | Girafa | Quero-Quero | Bicho Preguiça | Urso |
---|---|---|---|---|---|---|
- Crie um repositório git no gitlab.com
- Vamos definir um tipo de produto para cada time
- Sprint 1:
- Escolham uma feature do módulo de configuração e implemente os testes comuns e de exceção já apresentados em link.
#3 Avançado
Estruturação de testes reutilizáveis
Playwright Pytest Plugin
Para tornar os testes menos verbosos e otimizar o tempo de implementação o Playwright se integrou ao Pytest que fornece recursos para facilitar nossa vida, para tanto precisamos conhecer o funcionamento do Pytest inicialmente, então vamos lá!
# Avançado 3 - Estruturação de testes reutilizáveis
Fixtures - O que é?
import pytest
class Music:
def __init__(self, nome):
self.nome = nome
def __eq__(self, outra):
return self.nome == outra.nome
@pytest.fixture
def minha_musica():
return Music("Black")
@pytest.fixture
def playlist(minha_musica):
return [Music("Numb"), minha_musica]
def test_minha_musica_na_playlist(playlist):
assert Music('Numb') in playlist
Definindo de modo simplório é tudo aquilo necessário para realizar o teste. Elas são utilizadas pelo Pytest como exemplo abaixo:
O que acontece nessa Fixture?
Qual resultado desse teste?
# Avançado 3 - Estruturação de testes reutilizáveis
Fixtures Pytest - Escopos
- Escopo de função (
"function"
): A fixture é executada uma vez para cada função de teste. Isso é útil quando você precisa configurar um estado específico para cada teste individualmente. -
Escopo de classe (
"class"
): A fixture é executada uma vez para cada classe de teste. Isso é útil quando você precisa configurar um estado comum para todos os testes em uma classe. -
Escopo de módulo (
"module"
): A fixture é executada uma vez para cada módulo de teste. Isso é útil quando você precisa configurar um estado comum para todos os testes em um módulo. -
Escopo de sessão (
"session"
): A fixture é executada uma vez para toda a sessão de teste. Isso é útil quando você precisa configurar um estado comum para todos os testes em várias classes ou módulos.
# Avançado 3 - Estruturação de testes reutilizáveis
Exercício Escopos de Fixture
import pytest
# Configuração de sessão
@pytest.fixture(scope="session", autouse=True)
def setup_session():
print("Configuração de sessão")
# Opcionalmente, você pode retornar dados ou objetos úteis para os testes
yield
print("Desmontando sessão")
# Configuração de módulo
@pytest.fixture(scope="module", autouse=True)
def setup_module():
print("Configuração de módulo")
# Opcionalmente, você pode retornar dados ou objetos úteis para os testes
yield
print("Desmontando módulo")
# Configuração de classe
@pytest.fixture(scope="class", autouse=True)
def setup_class():
print("Configuração de classe")
# Opcionalmente, você pode retornar dados ou objetos úteis para os testes
yield
print("Desmontando classe")
# Configuração de função
@pytest.fixture(scope="function", autouse=True)
def setup_function():
print("Configuração de função")
# Opcionalmente, você pode retornar dados ou objetos úteis para os testes
yield
print("Desmontando função")
# Exemplo de teste
class TestExemplo:
def test1(self):
print("Executando o teste 1")
def test2(self):
print("Executando o teste 2")
# Execute os testes
pytest.main()
# Avançado 3 - Estruturação de testes reutilizáveis
O parâmetro autouse=True
garante que a fixture seja aplicada automaticamente a todos os testes sem a necessidade de chamá-la explicitamente.
Ao executar os testes, você verá que as configurações e desmontagens são realizadas de acordo com o escopo da fixture correspondente.
Prática Fixture
class Robo:
def __init__(self, nome, bateria=100):
self.nome = nome
self.bateria = bateria
def apresentar(self):
if self.bateria > 0:
self._consumir_bateria(5)
return f"Olá, eu sou o robô {self.nome}."
else:
return "Desculpe, estou sem energia para me apresentar."
def mover(self, direcao):
if self.bateria > 10:
self._consumir_bateria(10)
return f"Robô {self.nome} se moveu para {direcao}."
else:
return "Desculpe, estou sem energia para me mover."
def recarregar(self):
self.bateria = 100
return f"Robô {self.nome} recarregado."
def _consumir_bateria(self, quantidade):
self.bateria -= quantidade
# Avançado 3 - Estruturação de testes reutilizáveis
-
Neste exemplo, a classe
Robo
tem um construtor que recebe o nome do robô e uma quantidade inicial de bateria (padrão é 100). A classe possui os seguintes métodos:-
apresentar(): Retorna uma saudação se o robô tiver energia suficiente (consome 5 de bateria).
-
mover(direcao): Move o robô para uma direção específica se houver energia suficiente (consome 10 de bateria).
-
recarregar()
: Recarrega a bateria do robô para 100.
-
- O método
_consumir_bateria(quantidade)
é um método auxiliar privado que reduz a quantidade de bateria do robô quando chamado por outros métodos. - Crie uma classe de teste e verifique o comportamento dos métodos, utilizando fixtures.
Playwright - Fixtures
def test_my_app_is_working(fixture_name):
# Test using fixture_name
# ...
O Pytest Plugin para Playwright pode usar esses fixtures como argumento para a função de teste, conforme exemplo abaixo:
# Avançado 3 - Estruturação de testes reutilizáveis
Playwright - Fixtures - Funções
-
Esses acessórios são criados quando solicitados em uma função de teste e destruídos quando o teste termina.
-
context: Novo contexto do navegador para um teste
-
page: Nova página do navegador para um teste
-
# Avançado 3 - Estruturação de testes reutilizáveis
Playwright - Fixtures - Sessão
# Avançado 3 - Estruturação de testes reutilizáveis
-
Esses acessórios são criados quando solicitados em uma função de teste e destruídos quando todos os testes terminam.
- playwright: Playwright instância.
- browser_type: BrowserType instância do navegador atual.
- browser: Browser instância lançada pelo Playwright.
- browser_name: Nome no navegador.
- browser_channel: Canal do navegador
- is_chromium, is_webkit, is_firefox: Boleano para o navegador corrente
Playwright - Fixtures - Personalizando Opções
-
Para acessórios de navegador e contexto, use as fixtures a seguir para definir opções de inicialização personalizadas.
- browser_context_args: Substituir as opções para browser.new_context(**kwargs). Deve retornar um Dict.
- browser_type_launch_args: Substituir argumentos de inicialização para browser_type.launch(**kwargs). Deve retornar um Dict.
# Avançado 3 - Estruturação de testes reutilizáveis
Isolando Teste
from playwright.sync_api import Page
def test_basic_test(page: Page):
# ...
As páginas são isoladas entre os testes devido ao Browser Context, que é equivalente a um novo perfil de navegador, onde cada teste obtém um novo ambiente, mesmo quando vários testes são executados em um único navegador.
# Avançado 3 - Estruturação de testes reutilizáveis
Prática - Isolando teste
from playwright.sync_api import Page
def test_basic_test(page: Page):
# ...
- Sprint 2
- Traga os testes já criados na Sprint 1 para a estrutura de fixtures do Playwright
# Avançado 3 - Estruturação de testes reutilizáveis
Page Objects
Grandes suítes de teste podem ser estruturadas para otimizar a facilidade de criação e manutenção. Modelos de objeto por página é uma dessas abordagens para estruturar seu conjunto de testes.
# Avançado 3 - Estruturação de testes reutilizáveis
Page Objects
A estrutura de pacotes deve ficar similar ao modelo abaixo
# Avançado 3 - Estruturação de testes reutilizáveis
pages
|__ __init__.py
|__ auth.py
|__ channel.py
tests
|__ test_auth.py
|__ test_channel.py
.gitignore
README.md
requirements.txt
Page Objects
# pages/search.py
class SearchPage:
def __init__(self, page):
self.page = page
def navigate(self):
self.page.goto("https://bing.com")
def search(self, text):
self.search_term_input = self.page.locator("[name='q']")
self.search_term_input.fill(text)
self.search_term_input.press("Enter")
# test_search.py
from pages.search import SearchPage
from playwright.sync_api import Page, expect
# No teste
class TestBing:
def test_search_bing(self, page: Page):
search_page = SearchPage(page)
search_page.navigate()
search_page.search("search query")
page.wait_for_selector('text=resultados')
expect(page.get_by_text("resultados").first).to_be_visible()
# Avançado 3 - Estruturação de testes reutilizáveis
Page Objects
# pages/search.py
class SearchPage:
def __init__(self, page):
self.page = page
@property
def page_title(self):
return self.page.title()
def navigate(self):
self.page.goto("https://bing.com")
self.page_title()
# Avançado 3 - Estruturação de testes reutilizáveis
Utilize propriedades (get e set) para mapear elementos específicos ou criar estruturas padrões para uma determinada página.
Playwright Pytest Plugin - CLI
pytest <script/module> --browser webkit --headed
Para executar seus testes, use Pytest CLI.
--headed
: Executar os testes com interface gráfica (padrão: headless).
--browser
: Executar o teste no chromium
, firefox
, ou webkit
. Pode ser especificado várias vezes (padrão: todos os navegadores)
--browser-channel
Browser channel para se utilizar.
--slowmo
Executar o teste com atraso (milissegundos).
--device
Device to be emulated.
--output
Directory for artifacts produced by tests (default: test-results
).
--tracing
Se deve registrar o rastreio do teste após cada teste. on
, off
, or retain-on-failure
(padrão: off
).
--video
Se deve gravar o teste após cada teste. on
, off
, or retain-on-failure
(padrão: off
).
--screenshot
Se deve capturar automaticamente uma captura de tela após cada teste. on
, off
, ou only-on-failure
(padrão: off
).
# Avançado 3 - Estruturação de testes reutilizáveis
Page Objects
# test/conftest.py
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args, playwright):
playwright.selectors.set_test_id_attribute("data-test-id")
playwright.chromium.launch(headless=False)
return {
**browser_context_args,
"storage_state": "storage.json",
"viewport": {
"width": 1920,
"height": 1080,
}
}
# Avançado 3 - Estruturação de testes reutilizáveis
Uma prática comum ao criar essa estrutura de page objects é ter um módulo responsável por configurações genéricas do teste, para isso podemos criar o arquivo abaixo:
# test/conftest.py
import pytest
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args, playwright):
iphone_12 = playwright.devices['iPhone 12 Pro']
return {
**browser_context_args,
**iphone_12,
}
Pytest CLI - Praticando
# Emulando Device
pytest --device="iPhone 12 Pro"
# Executando teste em slowmotion (Retarda as operações em 1 segundo.)
pytest --slowmo 1000
# Executando teste no navegador presente na máquina
# Os canais disponíveis são chrome, msedge, chrome-betaou msedge-beta.msedge-dev
pytest --browser-channel chrome
# O base-url é usado para permitir que você defina o URL base da configuração
pytest --base-url http://localhost:8080
# Avançado 3 - Estruturação de testes reutilizáveis
- Para o Google Chrome, Microsoft Edge e outros navegadores baseados no Chromium, por padrão, o Playwright usa compilações do Chromium de código aberto. Como o projeto Chromium está à frente dos navegadores de marca, quando o mundo estiver no Google Chrome N, a Playwright já oferece suporte ao Chromium N+1 que será lançado no Google Chrome e Microsoft Edge algumas semanas depois.
- Se o Google Chrome ou o Microsoft Edge não estiverem disponíveis em sua máquina, você pode instalá-los usando a ferramenta de linha de comando Playwright:
playwright install msedge
- As instalações do Google Chrome ou do Microsoft Edge serão instaladas no local global padrão do seu sistema operacional, substituindo a instalação atual do navegador.
Pytest - Definindo parâmetros iniciais
# pytest.ini
[pytest]
addopts =
--browser chromium
--headed
--slowmo 1000
# Exeuctar
pytest
# Avançado 3 - Estruturação de testes reutilizáveis
O Pytest oferece para os testes com Playwright a possibilidade de configurar um arquivo com os parâmetros testados anteriormente
#4 Avançado
Integração com sistemas de build e CI/CD
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
Integração Contínua ou CI é uma prática importante no desenvolvimento de software. CI é uma abordagem em que as alterações de código são integradas em um repositório compartilhado com frequência, geralmente várias vezes ao dia. Cada vez que uma alteração é feita, o sistema de CI automaticamente constrói e testa o código para verificar se ele ainda funciona corretamente.
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
Existem várias razões pelas quais executar testes em CI é benéfico, vou citar alguns:
- Detecção rápida de problemas
- Garantia de qualidade contínua
- Feedback rápido
- Confiança na entrega
- Facilita a colaboração em equipe
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
É importante cuidar da qualidade do nosso código de teste, uma vez que ele bem mantido agiliza o desenvolvimento de testes e facilita a manutenção. Para isso vamos inserir a ferramenta de análise estática Flake8 no projeto conforme comando abaixo.
pip install flake8
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
O Flake8 ajuda a manter um código Python consistente e legível ao aplicar regras de estilo de código. Ele verifica se o código está em conformidade com as diretrizes definidas no PEP 8, como indentação correta, comprimento de linha, uso adequado de espaços em branco, nomenclatura de variáveis e funções, entre outros.
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
Para executar a análise do código execute o comando abaixo:
# Comando base
flake8
# Comando com opções log do flake com verbose, erros com statistics e traços de código
flake8 --verbose --statistics --show-source
Integração Contínua
# Avançado 4 - Integração com sistemas de build e CI/CD
Após instalar é importante configurar algumas validações que o mesmo deve realizar, ou não, no seu projeto de teste, para tanto crie um arquivo chamado .flake8 na raiz do projeto e insira o conteúdo abaixo:
[flake8]
#Ignorar um aviso ou erro específico para ignorar:
extend-ignore = E203
#Ignorar a verificação nessas pastas
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,venv
#Valor máximo de complexidade ciclomática permitido para um bloco de código.
max-complexity = 10
#Quantidade de caracteres por linha
max-line-length = 120
Lembre-se de que é recomendável manter a complexidade ciclomática baixa, geralmente abaixo de um limite específico, como 10 ou 15, para facilitar a leitura, a compreensão e a manutenção do código. Veja no link mais detalhes
Docker + Playwright
# Baixar imagem com pytest-playwright versão 1.35
docker pull mcr.microsoft.com/playwright/python:v1.35.0-jammy
Para aplicar a abordagem de Integração contínua utilizando Playwright podemos utilizar como apoio a plataforma Docker que auxilia no provisionamento do ambiente de desenvolvimento e teste em plataformas que oferecem o recurso de CI como Gitlab e GitHub
# Avançado 4 - Integração com sistemas de build e CI/CD
Docker + Playwright - Praticar
# Linux
docker run -it -v $(pwd):/projeto mcr.microsoft.com/playwright/python:v1.36.0-jammy bash
# Windows
docker run -it -v .:/projeto mcr.microsoft.com/playwright/python:v1.36.0-jammy bash
1. Vamos acessar o container em modo interativo para testar:
# Avançado 4 - Integração com sistemas de build e CI/CD
2. Agora acesse a pasta projeto em /projeto e instale as bibliotecas python presentes no requirements.txt.
3. Execute o teste
Docker + Playwright
# Dockerfile_dep
FROM mcr.microsoft.com/playwright/python:v1.36.0-jammy as playwright_base_image
LABEL authors="Vanilton Pinheiro"
ENV APP_PATH /usr/src/app
RUN mkdir $APP_PATH
WORKDIR $APP_PATH
COPY ./requirements.txt $APP_PATH
RUN python -m pip install --upgrade pip
RUN pip install -r requirements.txt
Para agilizar a execução da pipeline podemos manter uma imagem base com as bibliotecas do nosso projeto, segue o exemplo de um Dockerfile
# Avançado 4 - Integração com sistemas de build e CI/CD
# Construindo a imagem com dependências do projeto de teste
docker build -f Dockerfile_dep -t playwright_base_image .
# .dockerignore
venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
Ignorar pastas e arquivos serem adicionados na imagem construída
Docker + Playwright
FROM playwright_base_image
LABEL authors="Vanilton Pinheiro"
COPY . $APP_PATH
Com a imagem base criada agora podemos utilizar a imagem com todas as bibliotecas pronta apenas para executar o código fonte, para isso é necessário apenas copiar o código de teste e fazer a execução do container
# Avançado 4 - Integração com sistemas de build e CI/CD
# Construindo a imagem para execução dos testes
docker build -t playwright_run_image .
# Executando os testes dentro de um container baseado na imagem criada
docker run playwright_run_image pytest
# Executando utilizando volume (sem build de nova imagem)
# Linux
docker run -v $(pwd):/usr/src/app playwright_base_image pytest
# Windows
docker run -v .:/usr/src/app playwright_base_image pytest
Docker + Playwright
Lembre-se que todas essas imagens foram criadas localmente, logo para utilizá-las em um CI na nuvem é necessário fazer o push das imagens para um registry acessível pelo CI.
# Avançado 4 - Integração com sistemas de build e CI/CD
# Autenticar no registry do Docker Hub
docker login registry-1.docker.io
# Enviar imagem para o registry
docker image push <nome_imagem>:<tag>
Gitlab CI
# Avançado 4 - Integração com sistemas de build e CI/CD
Gitlab CE ou EE
Gitlab Runner
Gitlab CI
- Para utilizar o CI devemos criar um repositório no gitlab.com
- Habilitar um Runner para o Projeto (Específico ou Compartilhado)
- Criar o workflow da pipeline via arquivo .gitlab-ci.yml
# Avançado 4 - Integração com sistemas de build e CI/CD
Gitlab - Criando seu Runner
Caso deseje utilizar um Runner em sua infraestrutura para executar os testes, podemos configurar o mesmo, para isso:
- Instalar o gitlab-runner via Docker ou Binário (em seu próprio SO)
- Registrar o seu gitlab-runner com chave gerada no gitlab
# Avançado 4 - Integração com sistemas de build e CI/CD
Gitlab CI - Criando token
# Avançado 4 - Integração com sistemas de build e CI/CD
Gitlab CI - Registrando o Runner (binário)
# Avançado 4 - Integração com sistemas de build e CI/CD
Gitlab CI - Registrando o Runner Docker
# Avançado 4 - Integração com sistemas de build e CI/CD
docker volume create gitlab-runner-config
docker run -d --name gitlab-runner --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v gitlab-runner-config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest
docker run --rm -it -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest register
# Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com/
# Enter the registration token: (Gerar token linux)
token
# Enter an executor: docker+machine, kubernetes, custom, ssh, docker-autoscaler,
# shell, virtualbox, instance, docker, docker-windows, parallels:
docker
# Enter the default Docker image (for example, ruby:2.7):
ubuntu:20.04
1. Criar um volume
2. Iniciar container
3. Registrar o Runner
Gitlab CI - Checando Runner ativo
# Avançado 4 - Integração com sistemas de build e CI/CD
Docker + Playwright + Gitlab CI
Com o projeto de teste funcionando agora podemos colocá-los no repositório do gitlab.com e executá-lo via pipeline por meio do uso de imagem docker utilizando Runners compartilhados pelo serviço do Gitlab.com
# Avançado 4 - Integração com sistemas de build e CI/CD
# .gitlab-ci.yml
stages:
- code_analysis
- test
flake8:
stage: code_analysis
image: registry.gitlab.com/pipeline-components/flake8:latest
script:
- flake8 --verbose --statistics --show-source
chrome:
stage: test
image: mcr.microsoft.com/playwright/python:v1.35.0-jammy
script:
- python -m pip install --upgrade pip
- pip install -r requirements.txt
- pytest -s -v --junitxml=test-results.xml
artifacts:
when: always
reports:
junit: test-results.xml
Docker + Playwright + GitHub Actions
No GitHub a estratégia não se torna muito diferente do modelo criado no Gitlab, veja o modelo ao lado de Action criada para execução de pipeline no GitHub.
# Avançado 4 - Integração com sistemas de build e CI/CD
# .github/workflows/main.yml
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
playwright:
name: 'Playwright Tests'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright/python:v1.35.0-jammy
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run your code analysis
run: flake8 --verbose --statistics --show-source
- name: Run your tests Chrome
run: pytest -s -v -r test --junitxml=test-results.xml
- name: Run your tests Webkit
run: pytest -s -v -r test --browser webkit --junitxml=test-results-webkit.xml
- name: Upload test results chrome
uses: actions/upload-artifact@v3
with:
name: Test Results
path: test-results.xml
- name: Upload test results webkit
uses: actions/upload-artifact@v3
with:
name: Test Results
path: test-results-webkit.xml
- name: Surface failing tests chrome
if: always()
uses: pmeier/pytest-results-action@main
with:
path: test-results.xml
summary: true
display-options: fEX
fail-on-empty: true
- name: Surface failing tests webkit
if: always()
uses: pmeier/pytest-results-action@main
with:
path: test-results-webkit.xml
summary: true
display-options: fEX
fail-on-empty: true
#5 Avançado
Geração de relatórios de teste avançados
Relatório via Gitlab CI
# Avançado 5 - Geração de relatórios de teste avançado
Relatório com Pytest HTML
# Instalar a depdendência
pip install pytest-html
# Gerando com arquivos css e html separados
pytest --html=report.html
# Gerando todo conteúdo no arquivo html
pytest --html=report.html --self-contained-html
# Estilisando seu report
pytest --html=report.html --css=report_theme.css
# Avançado 5 - Geração de relatórios de teste avançado
Relatório com Playwright Trace
# Rastreando, gravando e printando os passos quando havendo falha
pytest --tracing=retain-on-failure
# Gera o video do cenário
pytest --video=retain-on-failure --screenshot=only-on-failure
# Gera o print screen da tela
pytest --screenshot=only-on-failure
# Para alterar a pasta e caminho padrão (test-results)
pytest --output <pasta>
# Avançado 5 - Geração de relatórios de teste avançado
Relatório com Playwright Trace
# Visualizando resultado do trace com falha
playwright show-trace test-results/**/trace.zip
# Avançado 5 - Geração de relatórios de teste avançado
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Allure é uma ferramenta de relatório de teste leve e flexível em vários idiomas projetada para criar relatórios de teste sofisticados e claros.
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
O Allure funciona conforme imagem abaixo:
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Para utilizá-lo vamos seguir os seguintes passos:
- Subir o Server e UI do Allure
- Instalar biblioteca para gerar o resultado allure
- Gerar o report de teste e postar no server Allure
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Server e UI Allure
# docker-compose.yml
version: '3'
services:
allure:
image: "frankescobar/allure-docker-service"
environment:
CHECK_RESULTS_EVERY_SECONDS: 1
KEEP_HISTORY: 1
ports:
- "5050:5050"
volumes:
- ${PWD}/allure-results:/app/allure-results
- ${PWD}/allure-reports:/app/default-reports
allure-ui:
image: "frankescobar/allure-docker-service-ui"
environment:
ALLURE_DOCKER_PUBLIC_API_URL: "http://localhost:5050"
ALLURE_DOCKER_PUBLIC_API_URL_PREFIX: ""
ports:
- "5252:5252"
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Instalar e Publicar resultado de teste com Allure
# Biblioteca
pip install allure-pytest
# Gerando o resultado do teste
pytest --alluredir=allure-results
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Publicar resultado de teste com Allure via API, pode-se utilizar o script abaixo ou algum presente neste endereço: https://github.com/fescobar/allure-docker-service/tree/master/allure-docker-api-usage
import os, requests, json, base64
# This directory is where you have all your results locally, generally named as `allure-results`
allure_results_directory = '/allure-results2'
# This url is where the Allure container is deployed. We are using localhost as example
allure_server = 'http://localhost:5050'
# Project ID according to existent projects in your Allure container -
# Check endpoint for project creation >> `[POST]/projects`
project_id = 'default'
# project_id = 'my-project-id'
# current_directory = os.path.dirname(os.path.realpath(__file__)) - Caso arquivo na raiz do projeto
current_directory = os.path.abspath(r"..")
results_directory = current_directory + allure_results_directory
print('RESULTS DIRECTORY PATH: ' + results_directory)
files = os.listdir(results_directory)
print('FILES:')
results = []
for file in files:
result = {}
file_path = results_directory + "/" + file
print(file_path)
if os.path.isfile(file_path):
try:
with open(file_path, "rb") as f:
content = f.read()
if content.strip():
b64_content = base64.b64encode(content)
result['file_name'] = file
result['content_base64'] = b64_content.decode('UTF-8')
results.append(result)
else:
print('Empty File skipped: ' + file_path)
finally :
f.close()
else:
print('Directory skipped: ' + file_path)
headers = {'Content-type': 'application/json'}
request_body = {
"results": results
}
json_request_body = json.dumps(request_body)
ssl_verification = True
print("------------------SEND-RESULTS------------------")
response = requests.post(allure_server + '/allure-docker-service/send-results?project_id=' + project_id, headers=headers, data=json_request_body, verify=ssl_verification)
print("STATUS CODE:")
print(response.status_code)
print("RESPONSE:")
json_response_body = json.loads(response.content)
json_prettier_response_body = json.dumps(json_response_body, indent=4, sort_keys=True)
print(json_prettier_response_body)
Relatório com Allure Report
# Avançado 5 - Geração de relatórios de teste avançado
Acessando o resultado
http://localhost:5252/allure-docker-service-ui/projects/default/reports/latest
Configure as categorias via categories.json
#6 Avançado
Otimização de testes para desempenho e velocidade
Paralelismo de teste
# install dependency
pip install pytest-xdist
# use the --numprocesses flag
pytest --numprocesses auto
Utilizando o Pytest podemos paralelizar os testes, ou seja, executando vários ao mesmo tempo. Para isso instale a biblioteca abaixo:
# Avançado 6 - Otimização de testes para desempenho e velocidade
Dependendo do hardware e da natureza de seus testes, você pode definir numprocesses para qualquer valor entre 2 e o número de CPUs na máquina.
Deixando em auto ele irá buscar a quantidade de cores de sua máquina, criando um worker para cada.
API Requests
from playwright.sync_api import Playwright, APIRequestContext, expect
from typing import Generator
@pytest.fixture(scope="session")
def api_request_context(
playwright: Playwright,
) -> Generator[APIRequestContext, None, None]:
request_context = playwright.request.new_context(
base_url="http://localhost:8000/"
)
yield request_context
request_context.dispose()
def test_request(api_request_context: APIRequestContext) -> None:
body = """
{'key': 'value'}
"""
response = api_request_context.post(f"resource/", data=body)
expect(response).to_be_ok()
print(response.json())
Para agilizar pré-condições é possível realizar requests via Playwright para preparar ambientes de teste.
# Avançado 6 - Otimização de testes para desempenho e velocidade
vanilton18@gmail.com
Vanilton Pinheiro
Referências >>
- Aplicações
- Bibliotecas
- Outros
- Ferramentas apoio ao conteúdo
Automação de Teste com Playwright e Python
By Vanilton Pinheiro
Automação de Teste com Playwright e Python
- 458