Automação de Teste com Playwright

Vanilton Pinheiro

vanilton.net

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

  1. Python 3.7+
  2. IDE PyCharm CE ou Professional
  3. Docker
  4. SO:
    1. Windows
    2. Linux
    3. 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 
  1. Os testes automatizados tem objetivo principal de reduzir esforço em testes manuais.
  2. Feedback rápido
  3. Redução de Bugs
  4. Confiança na solução
  5. 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 
  1. O que garante o valor de negócio esperado pelo usuário (fórmulas, áreas críticas)
  2. O que não necessita da avaliação por percepção humana
  3. O que simula realmente os passos do usuário

 

Quando automatizar?

  1. Quando existe ganho de custo de tempo a médio ou longo prazo frente o teste manual
  2. Quando tarefas manuais se tornam complexas com o tempo
  3. Quando se possui muitos ambientes ou fatores de teste
  4. Quando existe estabilidade na área a ser testada
# 1 Introdução Automação de Teste 

Quando não automatizar?

  1. Quando existe instabilidade na área a ser testada
  2. Limitação ou dependência de ferramenta
  3. Quando o custo de pessoas especialistas para a atividade é alto
  4. 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?

  1. Deve ser obrigatoriamente implementado através de alguma ferramenta que simule as operações do usuário.
  2. 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:

  1. Criar um novo Projeto
  2. Adicionar um python file que já é oferecido o modelo abaixo:

Python Unittest - Importante saber

# 2 Entendendo a Estrutura de Teste com Unittest 
  1. O Unittest utiliza da orientação a objeto
  2. Uma classe de teste com Unittest
    1. Herda de unittest.TestCase
    2. Pode conter vários casos de teste.
  3. 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 é:

  1. 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)

  2. Invocar o método em teste com os  parâmetros desejados e armazenar o valor de retorno

  3. 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:

  1. Identificar o método a ser testado

  2. Compreender a especificação do método: o que recebe de entrada e qual a saída produzida em função da entrada escolhida.

  3. 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 
  1. É uma boa prática de escrita de casos de teste unitário, separar cada caso de teste em um método de teste.

  2. 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 
  1. Clonar / baixar a branch main em https://github.com/Vanilton18/xpto-exemplos-teste 

  2. Verifique o comportamento do método check_valid_identifier na Classe Validators no pacote apps

  3. 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 
  1. Verifique o comportamento do método check_password na Classe Validators no pacote apps

  2. 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 
  1. O Playwright foi criado especificamente para acomodar as necessidades de testes de ponta a ponta, famoso e2e.

O que é?

# 3 Conhecendo o Playwright 
  1. É 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 
  1. Playwright fornece automação entre navegadores por meio de uma única API.

  2. O Playwright inicia navegadores sem periféricos (headless) por padrão.

  3. 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 
  1. Taxa de transferência = Gasta menos ciclos de CPU
  2. Latência = Executar teste rápido
  3. 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

  1. Acesse: https://vanilton.net/web-test/input-types/
  2. 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

  1. Acesse: https://vanilton.net/web-test/text/
  2. 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

  1. Acesse: https://vanilton.net/web-test/triangulo
  2. Faça o fluxo para identificar:
    1. Triângulos válidos:
      • escaleno
      • equilátero
      • isósceles
    2. Triângulo inválido
  3. 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

  1. Acesse: https://vanilton.net/web-test/triangulo_v2/
  2. 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")
  1. O seletor de texto localiza os elementos que contêm o texto passado.
  2. 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

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()
  1. O mecanismo CSS acessa o DOM aberto por padrão.
  2. 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"))
  1. Acesse: http://vanilton.net/web-test/display/
  2. 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"))
  1. 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())
  1. Selecionando elementos que correspondem a uma das condições​ (OR)

Onde esta solução pode ser interessante?

# 4 Localizadores

Localizadores XPATH

  1. XPath pode ser usado para navegar pelos elementos e atributos em um documento XML.

  2. 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

  1. XPath significa XML Path Language

  2. XPath usa a sintaxe "path like" para identificar e navegar em nós em um documento

  3. XML XPath contém mais de 200 funções integradas

  4. XPath é um elemento importante no padrão XSLT

  5. XPath é uma recomendação do W3C

  6. Arquivos HTML seguem o padrão XML, logo...

# 4 Localizadores

Localizadores XPATH

  1. Os seletores XPath são equivalentes a chamar Document.evaluate. Exemplo: xpath=//html/body.

  2. O seletor começando com // ou .. é considerado um seletor xpath. Por exemplo, o Playwright converte '//html/body' para 'xpath=//html/body'.

  3. 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
  1. 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
  1. 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]
  1. 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

  1. Crie o registro de uma pessoa
  2. Crie um XPATH ou CSS para acessar o botão de edição de acordo com o nome ou email da pessoa
  3. Edite o Nome ou Email da pessoa
  4. 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:

  1. Procuram os elementos no DOM na ordem de iteração
  2. 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

  1. 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

  1. Implemente um localizador para identificar os elementos à direita do botão com texto 'Tab 2' e em seguida retorne o texto do primeiro elemento.
  2. Faça o mesmo para a esquerda.
  3. Implemente um localizador para retornar a quantidade de elementos acima e abaixo do botão com texto 'Tab 2'
  4. 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)

  1. Implemente um localizador por posição para acessar o primeiro input com o atributo name 'num1' 
  2. 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 

  1. Implemente utilizando os comandos de interação os seguintes cálculos pela aplicação:
    1. Multiplicação  de 2 x 4.
    2. Implemente a raiz quadrada de 4
    3. 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

  1. Implemente a captura do resultado dos cálculos.
  2. 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

  1. Vamos simular algumas teclas e obter o resultado do card 'Event Dump'
  2. 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

  1. Selecione um radio button e verifique o resultado na div com id 'output'
  2. Marque os radios buttons e verifique a saída na div com id 'output-check'
  3. 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

  1. Crie uma função para capturar todas as imagens dos elementos <img> da loja
  2. Salve as imagens numa pasta chamada 'images'
  3. 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

  1. Implemente o login no site http://lojafake.vanilton.net/​
    1. Usuário: admin
    2. Senha: 1234
  2. Acesse o menu 'Minhas Listas'
  3. 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

  1. playwright show-trace trace.zip
  2. trace.playwright.dev

Local

Nuvem

# 5 Funções e Eventos 

Prática

  1. Adicione o trace para rastrear os passos do último exercício.
  2. 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

  1. Implemente um teste para a funcionalidade de cadastro de um usuário verificando sua inclusão na tabela
  2. Implemente a remoção de um usuário, verificando sua exclusão da tabela.

 

# 5 Funções e Eventos 
  1. Browser Contexts fornecem uma maneira de operar várias sessões de navegador independentes.
  2. 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

  1. Crie um script para carregar o contexto com o timezone de Nova York, locale da Inglaterra
  2. Acesse sua localização
  3. 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

  1. Implemente o upload de uma imagem
  2. Verifique a url gerada pelo nome do arquivo.
  3. Exiba em um print() a url da imagem.
  4. Crie um método que verifique a regra de arquivo maior que 500KB
# 5 Funções e Eventos 

Prática

  1. Implemente o upload de 2 ou mais arquivos
  2. Verifique se os mesmos serão exibido na listagem
  3. Limpe a lista de upload
  4. 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
  1. Como já conseguimos validar utilizando o modo interativo, podemos então criar nosso primeiro script no PyCharm.
  2. 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. 
  1. Acesse: https://vanilton.net/web-test/todos/
  2. Insira 3 tarefas
  3. Complete uma das tarefas adicionadas
  4. Acesse a lista de tarefas completadas
  5. 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
  1. Acesse: https://vanilton.net/web-test/todos/
  2. Insira 3 tarefas
  3. Complete uma das tarefas adicionadas
  4. Acesse a lista de tarefas Ativas (Active)
  5. 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:

  1. Acesse a lista de tarefas Completed
  2. 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
  1. 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:

  1. Acesse https://vanilton.net/web-test/triangulo_v2/
  2. Faça o fluxo de um triângulo equilátero.
  3. 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.

  1. Acesse https://vanilton.net/web-test/textarea/
  2. 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.

  1. Acesse https://vanilton.net/web-test/textarea/
  2. 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

  1. Na IDE PyCharm crie um Python Package com nome "tests"
  2. Neste pacote agora crie um script python "test_saleor.py"
  3. Acesse a página abaixo no seu navegador:
# Avançado 
2 - Fluxo de teste complexo

Aplicação de teste - Conhecendo e praticando

  1. Agora vamos autenticar:
    • Email address: admin@example.com

    • Password: admin

  2. 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

  1. Criar um localizador para identificar a lista de atividades

  2. Deve ser possível iterar nos itens da lista

  3. Criar uma lista de Ordens extraída da lista de atividades, considerar apenas o números das ordens, pode-se utilizar regex

  4. Abrir uma página nova e na tela de pesquisa de ordens, consultar os números das ordens contidas na lista

  5. 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
  1. Pré-condição para instalação
    • Git
    • Docker 
  2. 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

# 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
  1. Crie um repositório git no gitlab.com 
  2. Vamos definir um tipo de produto para cada time
  3. Sprint 1:
    1. Escolham uma feature do módulo de configuração e implemente os testes comuns e de exceção  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

  1. 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.
  2. 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.

  3. 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.

  4. 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
  1. 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.

  2. 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.
  3. 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.

# 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.
# 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):
  # ...
  1. 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:

  1. Detecção rápida de problemas
  2. Garantia de qualidade contínua
  3. Feedback rápido
  4. Confiança na entrega
  5. 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

  1. Para utilizar o CI devemos criar um repositório no gitlab.com
  2. Habilitar um Runner para o Projeto (Específico ou Compartilhado)
  3. 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:

  1. Instalar o gitlab-runner via Docker ou Binário (em seu próprio SO)
  2. 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:

  1. Subir o Server e UI do Allure
  2. Instalar biblioteca para gerar o resultado allure
  3. 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

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 >>

Automação de Teste com Playwright e Python

By Vanilton Pinheiro

Automação de Teste com Playwright e Python

  • 458