Communitygithub.com

windows-desktop-e2e

Testes E2E para aplicativos desktop nativos do Windows (WPF, WinForms, Win32/MFC, Qt) usando pywinauto e Automação de Interface do Windows (UIA). Use ao escrever testes E2E para um aplicativo desktop nativo do Windows com pywinauto ou UIA.

O que é windows-desktop-e2e?

Esta skill fornece um arcabouço completo para testes de ponta a ponta (E2E) de aplicativos desktop nativos do Windows, abrangendo os principais frameworks de interface gráfica: WPF, WinForms, Win32/MFC e Qt (5.x/6.x). Utilizando a biblioteca pywinauto com o backend de Automação de Interface do Windows (UIA), a skill oferece um guia detalhado sobre como configurar o ambiente de teste, definir localizadores robustos (priorizando AutomationId) e implementar o padrão Page Object Model (POM) para organizar o código de teste de forma modular e sustentável. Suporta capturas de tela automáticas em falhas, gravação de vídeo, rastreamento passo a passo para diagnóstico de testes instáveis (flaky), e integração com pipelines CI/CD como GitHub Actions. A skill também inclui conselhos específicos por framework para melhorar a testabilidade, como a adição de AutomationId em controles, além de orientações sobre quando não usar esta abordagem (por exemplo, para aplicações web ou Electron). É ideal para desenvolvedores e engenheiros de qualidade que precisam automatizar a verificação de interfaces desktop Windows de forma confiável e eficiente.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/windows-desktop-e2e

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Testes E2E para Desktop Windows

Testes de ponta a ponta para aplicativos desktop nativos do Windows usando pywinauto com o backend Automação de Interface do Windows (UIA). Abrange WPF, WinForms, Win32/MFC e Qt (5.x/6.x) — com orientações específicas para Qt em uma seção dedicada.

Quando Ativar

  • Escrevendo ou executando testes E2E para um aplicativo desktop nativo do Windows
  • Configurando uma suíte de testes de GUI desktop do zero
  • Diagnosticando testes de desktop automatizados instáveis ou com falha
  • Adicionando testabilidade (AutomationId, nomes acessíveis) a um aplicativo existente
  • Integrando testes E2E desktop em um pipeline CI/CD (GitHub Actions windows-latest)

Quando NÃO Usar

  • Aplicações Web → use a skill e2e-testing (Playwright)
  • Aplicações Electron/CEF/WebView2 → a camada HTML precisa de automação de navegador, não UIA
  • Aplicações Mobile → use ferramentas específicas da plataforma (UIAutomator, XCUITest)
  • Testes de unidade ou integração puros que não precisam de uma GUI em execução

Conceitos Fundamentais

Toda automação de desktop Windows depende da Automação de Interface do Windows (UIA), uma API de acessibilidade nativa do Windows. Cada framework suportado expõe uma árvore de elementos UIA com propriedades que o agente pode ler e nas quais pode atuar:

Seu teste (Python)
    └── pywinauto (backend UIA)
        └── API de Automação de Interface do Windows   ← nativa do Windows, independente de framework
            └── Provedor UIA do aplicativo              ← cada framework fornece o seu
                └── .exe em execução

Qualidade UIA por framework:

FrameworkAutomationIdConfiabilidadeNotas
WPF5/5Excelentex:Name mapeia diretamente para AutomationId
WinForms4/5BoaAccessibleName = AutomationId
UWP / WinUI 35/5ExcelenteSuporte completo da Microsoft
Qt 6.x5/5ExcelenteAcessibilidade habilitada por padrão; nomes de classe mudam para Qt6*
Qt 5.15+4/5BoaMódulo de Acessibilidade melhorado
Qt 5.7–5.143/5RazoávelPrecisa de QT_ACCESSIBILITY=1; objectName manual
Win32 / MFC3/5RazoávelIDs de controle acessíveis; correspondência de texto comum

Configuração e Pré-requisitos

# Python 3.8+, apenas Windows
pip install pywinauto pytest pytest-html Pillow pytest-timeout
# Opcional: gravação de tela
# Instale o ffmpeg e adicione ao PATH: https://ffmpeg.org/download.html

Verifique se o UIA está acessível:

from pywinauto import Desktop
Desktop(backend="uia").windows()  # lista todas as janelas de nível superior

Instale o Accessibility Insights for Windows (gratuito, da Microsoft) — sua ferramenta DevTools equivalente para inspecionar a árvore de elementos UIA antes de escrever qualquer teste.

Configuração de Testabilidade (por Framework)

A coisa mais impactante que você pode fazer é dar a cada controle interativo um AutomationId estável antes de escrever os testes.

WPF

<!-- XAML: x:Name torna-se AutomationId automaticamente -->
<TextBox x:Name="usernameInput" />
<PasswordBox x:Name="passwordInput" />
<Button x:Name="btnLogin" Content="Login" />
<TextBlock x:Name="lblError" />

WinForms

// Defina no designer ou no código
usernameInput.AccessibleName = "usernameInput";
passwordInput.AccessibleName = "passwordInput";
btnLogin.AccessibleName = "btnLogin";
lblError.AccessibleName = "lblError";

Win32 / MFC

// IDs de recurso de controle no arquivo .rc são expostos como strings AutomationId
// IDC_EDIT_USERNAME -> AutomationId "1001"
// Prefira SetWindowText para Name; adicione IAccessible para suporte mais rico

Qt — veja a seção dedicada abaixo


Modelo de Página (Page Object Model)

tests/
├── conftest.py          # fixture de inicialização do aplicativo, captura de tela em falha
├── pytest.ini
├── config.py
├── pages/
│   ├── __init__.py      # necessário para imports
│   ├── base_page.py     # localizadores, espera, helpers de captura de tela
│   ├── login_page.py
│   └── main_page.py
├── tests/
│   ├── __init__.py
│   ├── test_login.py
│   └── test_main_flow.py
└── artifacts/           # capturas de tela, vídeos, logs

base_page.py

import os, time
from pywinauto import Desktop
from config import ACTION_TIMEOUT, ARTIFACT_DIR

class BasePage:
    def __init__(self, window):
        self.window = window

    # --- Localizadores (ordem de prioridade) ---

    def by_id(self, auto_id, **kw):
        """AutomationId — mais estável. Use como primeira escolha."""
        return self.window.child_window(auto_id=auto_id, **kw)

    def by_name(self, name, **kw):
        """Texto visível / nome acessível."""
        return self.window.child_window(title=name, **kw)

    def by_class(self, cls, index=0, **kw):
        """Classe de controle + índice — frágil, evite se possível."""
        return self.window.child_window(class_name=cls, found_index=index, **kw)

    # --- Esperas ---

    def wait_visible(self, spec, timeout=ACTION_TIMEOUT):
        spec.wait("visible", timeout=timeout)
        return spec

    def wait_gone(self, spec, timeout=ACTION_TIMEOUT):
        spec.wait_not("visible", timeout=timeout)
        return spec

    def wait_window(self, title, timeout=ACTION_TIMEOUT):
        """Aguarda uma nova janela de nível superior (diálogos, janelas filhas)."""
        dlg = Desktop(backend="uia").window(title=title)
        dlg.wait("visible", timeout=timeout)
        return dlg

    def wait_until(self, fn, timeout=ACTION_TIMEOUT, interval=0.3):
        """Avalia uma condição arbitrária — use quando os eventos UIA não são confiáveis."""
        deadline = time.time() + timeout
        while time.time() < deadline:
            try:
                if fn():
                    return True
            except Exception:
                pass
            time.sleep(interval)
        raise TimeoutError(f"Condição não atendida em {timeout}s")

    # --- Ações ---

    def click(self, spec):
        self.wait_visible(spec)
        spec.click_input()

    def type_text(self, spec, text):
        self.wait_visible(spec)
        ctrl = spec.wrapper_object()
        try:
            ctrl.set_edit_text(text)
        except Exception as e:
            # Qt 5.x fallback: o Padrão de Valor UIA pode estar incompleto
            import sys, pywinauto.keyboard as kb
            print(f"[windows-desktop-e2e] set_edit_text falhou ({e}), usando fallback do teclado", file=sys.stderr)
            ctrl.click_input()
            kb.send_keys("^a")
            kb.send_keys(text, with_spaces=True)

    def get_text(self, spec):
        ctrl = spec.wrapper_object()
        for attr in ("window_text", "get_value"):
            try:
                v = getattr(ctrl, attr)()
                if v:
                    return v
            except Exception:
                pass
        return ""

    # --- Artefatos ---

    def screenshot(self, name):
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        path = os.path.join(ARTIFACT_DIR, f"{name}.png")
        self.window.capture_as_image().save(path)
        return path

login_page.py

from pages.base_page import BasePage

class LoginPage(BasePage):
    @property
    def username(self): return self.by_id("usernameInput")

    @property
    def password(self): return self.by_id("passwordInput")

    @property
    def btn_login(self): return self.by_id("btnLogin")

    @property
    def error_label(self): return self.by_id("lblError")

    def login(self, user, pwd):
        self.type_text(self.username, user)
        self.type_text(self.password, pwd)
        self.click(self.btn_login)

    def login_ok(self, user, pwd, main_title="Main Window"):
        self.login(user, pwd)
        return self.wait_window(main_title)

    def login_fail(self, user, pwd):
        self.login(user, pwd)
        self.wait_visible(self.error_label)
        return self.get_text(self.error_label)

conftest.py

Para novos projetos, prefira a fixture sandbox Nível 1 (veja abaixo) — ela adiciona isolamento do sistema de arquivos sem custo extra. Esta fixture básica é apenas para configurações mínimas/legadas.

import os, pytest
os.environ["QT_ACCESSIBILITY"] = "1"  # Necessário para suporte UIA no Qt 5.x

from pywinauto import Application
from config import APP_PATH, MAIN_WINDOW_TITLE, LAUNCH_TIMEOUT, ARTIFACT_DIR

@pytest.fixture
def app(request):
    if not APP_PATH:
        pytest.exit("A variável de ambiente APP_PATH não está definida", returncode=1)
    proc = Application(backend="uia").start(APP_PATH, timeout=LAUNCH_TIMEOUT)
    win  = proc.window(title=MAIN_WINDOW_TITLE)
    win.wait("visible", timeout=LAUNCH_TIMEOUT)
    yield win
    # Captura de tela em falha
    if getattr(getattr(request.node, "rep_call", None), "failed", False):
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        try:
            win.capture_as_image().save(
                os.path.join(ARTIFACT_DIR, f"FAIL_{request.node.name}.png")
            )
        except Exception:
            pass
    # Saída gracefull primeiro, encerramento forçado como fallback
    # proc é um Application do pywinauto — use wait_for_process_exit(), não wait_for_process()
    try:
        win.close()
        proc.wait_for_process_exit(timeout=5)
    except Exception:
        proc.kill()

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    setattr(item, f"rep_{outcome.get_result().when}", outcome.get_result())

config.py

import os
APP_PATH          = os.environ.get("APP_PATH", "")           # definido via env — sem caminho padrão
MAIN_WINDOW_TITLE = os.environ.get("APP_TITLE", "")
LAUNCH_TIMEOUT    = int(os.environ.get("LAUNCH_TIMEOUT", "15"))
ACTION_TIMEOUT    = int(os.environ.get("ACTION_TIMEOUT", "10"))
ARTIFACT_DIR      = os.path.join(os.path.dirname(__file__), "artifacts")

pytest.ini

[pytest]
testpaths = tests
markers =
    smoke: testes smoke rápidos para caminhos críticos
    flaky: testes conhecidamente instáveis
addopts = -v --tb=short --html=artifacts/report.html --self-contained-html

Estratégia de Localizadores

AutomationId  >  Nome (texto)  >  ClassName + índice  >  XPath
  (estável)         (legível)        (frágil)            (último recurso)

Inspecione com Accessibility Insights → painel Propriedades → procure por AutomationId primeiro.

# Inspecione em tempo de execução — cole em um REPL para explorar a árvore
win.print_control_identifiers()
# ou limite o escopo:
win.child_window(auto_id="groupBox1").print_control_identifiers()

Padrões de Espera

# Aguarda o controle aparecer
page.wait_visible(page.by_id("statusLabel"))

# Aguarda o controle desaparecer (ex.: spinner de carregamento)
page.wait_gone(page.by_id("spinnerOverlay"))

# Aguarda um diálogo aparecer
dlg = page.wait_window("Confirmar Exclusão")

# Condição personalizada (ex.: mudança de texto)
page.wait_until(lambda: page.get_text(page.by_id("lblStatus")) == "Pronto")

Nunca use time.sleep() como sincronização primária — use wait() ou wait_until().

Gerenciamento de Artefatos

# Captura de tela sob demanda
page.screenshot("apos_login")

# Captura de tela em tela cheia (quando a janela está fora da tela ou minimizada)
import pyautogui
pyautogui.screenshot("artifacts/fullscreen.png")

# Gravação de tela com ffmpeg (iniciar antes do teste, parar depois)
import subprocess

def iniciar_gravacao(nome):
    return subprocess.Popen([
        "ffmpeg", "-f", "gdigrab", "-framerate", "10",
        "-i", "desktop", "-y", f"artifacts/videos/{nome}.mp4"
    ], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def parar_gravacao(proc):
    proc.stdin.write(b"q"); proc.stdin.flush(); proc.wait(timeout=10)

Rastreamento por Etapa (opt-in)

A captura de tela padrão em falha é muitas vezes insuficiente para diagnosticar testes instáveis. O rastreamento em nível de etapa abaixo está desativado por padrão — ative-o apenas ao reproduzir um caso instável.

Ativar

E2E_TRACE=1 pytest tests/test_login.py -v
# Incluir texto digitado no log JSONL (NÃO use em testes que digitam credenciais/PII):
E2E_TRACE=1 E2E_TRACE_INCLUDE_TEXT=1 pytest ...

Integrar no BasePage

import os, json, time
TRACE_ENABLED      = os.environ.get("E2E_TRACE") == "1"
TRACE_INCLUDE_TEXT = os.environ.get("E2E_TRACE_INCLUDE_TEXT") == "1"

class BasePage:
    _step = 0

    def _trace(self, action, spec=None, text=None):
        if not TRACE_ENABLED:
            return
        BasePage._step += 1
        idx = f"{BasePage._step:03d}"
        os.makedirs(ARTIFACT_DIR, exist_ok=True)
        try:
            self.window.capture_as_image().save(
                os.path.join(ARTIFACT_DIR, f"step_{idx}_{action}.png"))
        except Exception:
            pass  # falha na captura não deve quebrar o teste
        rec = {
            "ts": time.time(), "step": BasePage._step, "action": action,
            "locator": getattr(spec, "criteria", None),
            "text": text if TRACE_INCLUDE_TEXT else ("<redacted>" if text else None),
        }
        with open(os.path.join(ARTIFACT_DIR, "trace.jsonl"), "a") as f:
            f.write(json.dumps(rec) + "\n")

    def click(self, spec):
        self.wait_visible(spec); self._trace("click_before", spec)
        spec.click_input();      self._trace("click_after",  spec)

    def type_text(self, spec, text):
        self.wait_visible(spec); self._trace("type_before", spec, text)
        # ... lógica existente de set_edit_text / fallback de teclado ...
        self._trace("type_after", spec)

Advertências

  • PII / credenciais: o conteúdo de type_text é <redacted> por padrão. Nunca defina E2E_TRACE_INCLUDE_TEXT=1 em fluxos de login ou pagamento.
  • Sobrecarga: ~50–200ms por ação + um PNG por etapa em disco. Não ative na matriz CI padrão — apenas em um trabalho dedicado de reprodução de instabilidade.
  • Inchaço de artefatos: um fluxo longo produz dezenas de MB; ajuste retention-days adequadamente.
  • Higiene de paralelismo/re-execução: este exemplo simples anexa a trace.jsonl e usa um contador em nível de classe. Limpe o diretório de artefatos antes de reexecuções e use diretórios de artefatos por worker para testes paralelos.
  • Lacuna de cobertura: ações executadas fora do BasePage (chamadas diretas a pywinauto no código de teste) não são rastreadas.

Tratamento de Testes Instáveis

# Quarentena — equivalente a test.fixme() do Playwright
@pytest.mark.skip(reason="Instável: race condition de animação em CI lenta. Issue #42")
def test_transicao_animada(self, app): ...

# Pular se uma condição não for atendida (ex.: recurso ausente)
@pytest.mark.skipif("--run-slow" not in sys.argv, reason="Teste lento; pule com --run-slow")
def test_carga_pesada(self, app): ...

Gradação de Reinicialização (Tier 1 Sandbox)

Em CI, um aplicativo desktop pode vazar estado entre testes. Para evitar isso, o fixture Tier 1 Sandbox abaixo recria o diretório de configuração do aplicativo antes de cada teste:

import os, shutil, tempfile, pytest
from pywinauto import Application

SANDBOX = os.environ.get("E2E_SANDBOX", "")

@pytest.fixture
def app_sandbox(request):
    """
    Fixture que cria um sandbox de sistema de arquivos isolado.
    Define os.path.expanduser para um diretório temporário,
    copia os dados de configuração necessários e inicia o aplicativo.
    """
    sandbox_dir = tempfile.mkdtemp(prefix="e2e_sandbox_")
    app_data_dir = os.path.join(sandbox_dir, "AppData")
    os.makedirs(app_data_dir)
    os.environ["USERPROFILE"] = sandbox_dir
    # Copie os dados de configuração iniciais, se houver
    # shutil.copytree("initial_config", app_data_dir, dirs_exist_ok=True)
    
    proc = Application(backend="uia").start(APP_PATH, timeout=LAUNCH_TIMEOUT)
    win = proc.window(title=MAIN_WINDOW_TITLE)
    win.wait("visible", timeout=LAUNCH_TIMEOUT)
    yield win
    
    try:
        win.close()
        proc.wait_for_process_exit(timeout=5)
    except Exception:
        proc.kill()
    finally:
        # Limpeza do sandbox pode ser adiada ou omitida em CI
        if not SANDBOX:
            shutil.rmtree(sandbox_dir, ignore_errors=True)

Análise Pós-Morte (Post-Mortem) com Hooks do Pytest

# No conftest.py — adicione ao fixture 'app' existente
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    report = outcome.get_result()
    if report.when == "call" and report.failed:
        print(f"\n--- INÍCIO PÓS-MORTE: {item.name} ---")
        print(f"  Duração: {report.duration:.2f}s")
        print(f"  Causa:   {report.longrepr}")
        # Salve informações do sistema, se aplicável
        print(f"--- FIM PÓS-MORTE ---")

Integração Contínua

GitHub Actions (windows-latest)

# .github/workflows/e2e-desktop.yml
name: Testes E2E Desktop
on: [push, pull_request]

jobs:
  e2e:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip"
      - name: Instalar dependências
        run: |
          pip install pywinauto pytest pytest-html Pillow pytest-timeout
      - name: Baixar/binário do aplicativo
        run: |
          # Substitua pela URL real de download do seu aplicativo
          Invoke-WebRequest -Uri "${{ secrets.APP_DOWNLOAD_URL }}" -OutFile "app_installer.exe"
          .\app_installer.exe /S /D=${{ github.workspace }}\app
          echo "APP_PATH=${{ github.workspace }}\app\MyApp.exe" >> $env:GITHUB_ENV
          echo "APP_TITLE=MyApp" >> $env:GITHUB_ENV
      - name: Executar testes E2E
        run: |
          pytest tests/ --html=artifacts/report.html --self-contained-html -v
      - name: Fazer upload de artefatos
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-artifacts
          path: artifacts/

Estratégia de Matriz para Múltiplos Binários

jobs:
  e2e:
    strategy:
      matrix:
        binary: [App-Stable.exe, App-Beta.exe]
    steps:
      - uses: actions/checkout@v4
      - ... # instalar dependências
      - name: Baixar binário: ${{ matrix.binary }}
        run: |
          Invoke-WebRequest -Uri "${{ secrets.APP_DOWNLOAD_URL }}${{ matrix.binary }}" -OutFile "${{ github.workspace }}\app.exe"
          echo "APP_PATH=${{ github.workspace }}\app.exe" >> $env:GITHUB_ENV
          echo "APP_TITLE=Aplicativo Teste" >> $env:GITHUB_ENV
      - name: Executar testes E2E
        run: pytest tests/ -v

Advertências e Observações

Ganchos de Janela e Antivírus

A automação UIA funciona via ganchos de janela do Windows. Alguns softwares antivírus (especialmente McAfee, Norton) ou ferramentas de produtividade (AutoHotkey, ferramentas de macro) podem interferir. Se suspeitar de interferência, execute o teste em um ambiente limpo (sandbox ou VM).

Permissões de Administrador

Se o aplicativo solicitar elevação de privilégios (executar como Administrador), o teste também precisará ser executado como Administrador. Isso é comum na CI apenas com configuração especial.

Ferramentas de Depuração de Acessibilidade

FerramentaPropósito
Accessibility InsightsInspecionar elementos UIA (equivalente ao DevTools do navegador)
Inspect.exeSDK do Windows — leve, mostra a árvore UIA bruta
UISpy.exeAlternativa open-source mais antiga

Lidando com Ctrl+Alt+Del / Tela de Bloqueio

Se um teste acionar a tela de segurança do Windows (ex.: Ctrl+Alt+Del), os testes falharão porque a automação UIA não pode cruzar essa fronteira de segurança. Evite testar essa interação ou garanta que ela não seja acionada acidentalmente.


Orientações Específicas para Qt

Aplicativos Qt precisam de atenção especial porque a pilha de acessibilidade depende da versão e das variáveis de ambiente.

Qt 6.x (Recomendado)

  • Acessibilidade habilitada por padrão.
  • Nomes de classe mudam de Qt5* para Qt6* (ex.: Qt6Edit, Qt6PushButton).
  • objectName mapeia para AutomationId.
  • Funciona com o backend UIA do pywinauto sem configuração extra.

Qt 5.15+

  • Defina QT_ACCESSIBILITY=1 antes de iniciar o aplicativo.
  • objectName geralmente mapeia para AutomationId, mas teste primeiro.
  • Alguns widgets podem precisar de subclasses para expor propriedades UIA corretas.

Qt 5.7–5.14

  • Sempre defina QT_ACCESSIBILITY=1 (como variável de ambiente ou no código).
  • objectName nem sempre mapeia para AutomationId; você pode precisar usar correspondência de texto.
  • Teste em um ambiente CI para garantir que o back-end UIA funcione.
  • Se set_edit_text falhar, o fallback de teclado em BasePage.type_text() é usado automaticamente.

Exemplo: Adicionando objectName no Qt Designer / C++

// No construtor do seu widget ou diálogo
ui->usernameEdit->setObjectName("usernameInput");
ui->passwordEdit->setObjectName("passwordInput");
ui->loginButton->setObjectName("btnLogin");
ui->errorLabel->setObjectName("lblError");

Depuração Qt

# Verifique se o módulo de acessibilidade Qt está carregado
import os
print("QT_ACCESSIBILITY =", os.environ.get("QT_ACCESSIBILITY", "não definido"))

# Inspecione a árvore UIA do aplicativo Qt
win.print_control_identifiers()

Se a árvore UIA estiver vazia ou incompleta, verifique:

  1. A variável de ambiente QT_ACCESSIBILITY está definida como 1?
  2. O aplicativo está compilado com suporte a acessibilidade? (Por padrão em Qt 5.15+ e Qt 6.x)
  3. Você está usando o backend "uia" do pywinauto? (vs. o backend "win32" padrão)

Migrando de pywinauto backend "win32" para "uia"

Se seu código existente usa o backend "win32" do pywinauto (o padrão), migrar para "uia" melhora muito a confiabilidade, especialmente com controles ricos. Veja o que muda:

Recursowin32uia
Localizador automáticoauto_id não suportadoauto_id suportado (AutomationId)
Caixas de textoset_edit_text lentoset_edit_text rápido
Tabelas/ListasSuporte limitadoSuporte completo a padrões
Barras de menuMenuSelectSuporte a itens de menu via automação

Para migrar:

  1. Mude Application(backend="uia") em sua fixture.
  2. Substitua chamadas window_() por child_window() com auto_id= onde possível.
  3. Teste cada página — alguns localizadores podem precisar de ajustes devido a mudanças na árvore.

Convenções de Nomenclatura

ItemConvençãoExemplo
AutomationIdcamelCasebtnLogin, usernameInput
Nome da página/classePascalCase + PageLoginPage, MainPage
nome do métodosnake_caselogin_ok, wait_visible
Nome da fixturesnake_case, apenas appapp, app_sandbox
Nome do arquivo de configuraçãosnake_caseconfig.py
Marcador de testesnake_case, texto curtosmoke, flaky

Metadados do Projeto

  • Versão: 1.0.0
  • Licença: MIT
  • Dependências: Python 3.8+, pywinauto, pytest, Pillow
  • Dependências opcionais: pytest-html, pytest-timeout, ffmpeg para gravação de tela

FAQ

Por que o set_edit_text falha no meu aplicativo Qt?

Isso geralmente acontece quando o Padrão de Valor UIA não está totalmente implementado. A classe BasePage.type_text() inclui um fallback automático de teclado para esses casos. Certifique-se de que QT_ACCESSIBILITY=1 está definido para Qt 5.x.

Como encontro o AutomationId de um controle?

Use o Accessibility Insights → clique em Inspect → clique no controle → procure no painel Propriedades por AutomationId. Se estiver vazio, o desenvolvedor precisa adicioná-lo no código-fonte.

Meu teste funciona localmente, mas falha no CI

Causas comuns:

  • Variáveis de ambiente: o binário do aplicativo ou argumentos de linha de comando não estão presentes.
  • Resolução de tela: CI pode ter uma resolução menor, movendo controles para fora da tela.
  • Escala DPI: O Windows CI pode estar com escala de 150%; use SetProcessDPIAware() no aplicativo ou configure o fixture de teste para DPI compatível.
  • Problemas de permissões: O aplicativo solicita elevação de administrador.
  • Caminho temporal: O aplicativo não inicia dentro do LAUNCH_TIMEOUT; aumente o timeout em CI.

Devo usar pywinauto ou pyautogui para cliques?

Use pywinauto para interagir com controles específicos (por exemplo, encontrar uma caixa de texto e digitar). Use pyautogui apenas para coordenadas de tela ou capturas de tela em tela cheia. Não misture ambos para localização de controles — pywinauto é mais confiável com UIA.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Habilidades Relacionadas