Skip to content

Commit 606d19d

Browse files
committed
ajustes
1 parent b3c345d commit 606d19d

2 files changed

Lines changed: 60 additions & 67 deletions

File tree

pages/BasePages.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,65 +18,62 @@ export class BasePage {
1818
try {
1919
console.log(`[BasePage] 🧭 Navegando para: ${url} (Tentativa ${attempt}/${maxRetries})`);
2020
const currentTimeout = attempt === 1 ? 5000 : 30000;
21-
2221
await this.page.goto(path, { waitUntil: 'domcontentloaded', timeout: currentTimeout });
2322
await this.page.waitForSelector('body', { timeout: 10000 });
2423
return;
25-
2624
} catch (error: any) {
2725
if (attempt === maxRetries) {
2826
console.error(`[BasePage] ❌ Falha final de conexão com ${url}`);
2927
throw error;
3028
}
31-
console.warn(`[BasePage] ⚠️ Falha na tentativa ${attempt} (${error.message.includes('Timeout') ? 'Timeout' : 'Erro'}): Retentando...`);
29+
console.warn(`[BasePage] ⚠️ Falha na tentativa ${attempt}: Retentando...`);
3230
await this.page.waitForTimeout(2000);
3331
}
3432
}
3533
}
3634

3735
async smartClick(selector: string, contextDescription: string) {
3836
try {
39-
// Tenta clicar normalmente
40-
await this.page.waitForSelector(selector, { state: 'visible', timeout: 5000 }); // Timeout curto para acionar o healing rápido
37+
// Tenta clicar com timeout curto (5s)
38+
await this.page.waitForSelector(selector, { state: 'visible', timeout: 5000 });
4139
await this.page.click(selector);
4240
} catch (error: any) {
43-
// Se falhar e não tiver token, explode erro normal
4441
if (!process.env.GITHUB_AI_TOKEN) throw error;
4542

46-
console.warn(`[Self-Healing] 🚑 Falha ao clicar em: '${contextDescription}'. Pedindo socorro à IA...`);
43+
console.warn(`[Self-Healing] 🚑 Falha ao clicar em: '${contextDescription}'. Chamando IA...`);
4744

4845
try {
4946
const cleanDom = await this.page.evaluate(() => {
50-
// Limpa scripts e styles para facilitar a leitura da IA
5147
return document.body ? document.body.innerHTML.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gm, "").substring(0, 15000) : "DOM Vazio";
5248
});
5349

5450
const failureMessage = error.message || String(error);
5551

56-
// Chama a IA
52+
console.log(`[Self-Healing] 📞 Contactando AIService...`);
5753
const analysis = await this.ai.analyzeFailure(failureMessage, cleanDom);
58-
59-
// 🔍 DEBUG: Ver o que a IA respondeu
60-
console.log(`[Self-Healing] 🤖 Resposta da IA: ${analysis}`);
54+
console.log(`[Self-Healing] 🤖 Retorno da IA: ${analysis}`);
6155

62-
// Extrai o conteúdo entre crases
63-
const suggestedSelector = analysis.match(/`([^`]+)`/)?.[1];
56+
// Regex mais flexível: Pega entre crases OU pega a última palavra se parecer um ID/Class
57+
const match = analysis.match(/`([^`]+)`/);
58+
let suggestedSelector = match ? match[1] : null;
59+
60+
// Fallback: Se a IA respondeu só "#login-button" sem crases
61+
if (!suggestedSelector && (analysis.startsWith("#") || analysis.startsWith("."))) {
62+
suggestedSelector = analysis.trim();
63+
}
6464

65-
if (suggestedSelector) {
66-
console.log(`[Self-Healing] ✅ Seletor encontrado: ${suggestedSelector}. Aplicando correção...`);
67-
68-
// Tenta clicar no NOVO seletor sugerido
65+
if (suggestedSelector && suggestedSelector !== "null") {
66+
console.log(`[Self-Healing] ✅ Tentando novo seletor: ${suggestedSelector}`);
6967
await this.page.waitForSelector(suggestedSelector, { state: 'visible', timeout: 5000 });
7068
await this.page.click(suggestedSelector);
71-
72-
console.log(`[Self-Healing] ✨ SUCESSO! O teste foi curado automaticamente.`);
69+
console.log(`[Self-Healing] ✨ SUCESSO! Correção aplicada.`);
7370
} else {
74-
console.error(`[Self-Healing] ❌ A IA não conseguiu sugerir um seletor válido.`);
75-
throw error; // Relança o erro original se a IA falhar
71+
console.error(`[Self-Healing] ❌ IA não retornou um seletor válido.`);
72+
throw error;
7673
}
7774
} catch (aiError) {
78-
console.error(`[Self-Healing] 💀 Falha crítica no processo de cura: ${aiError}`);
79-
throw error; // Garante que o teste falhe se o healing quebrar
75+
console.error(`[Self-Healing] 💀 Erro no processo de cura: ${aiError}`);
76+
throw error;
8077
}
8178
}
8279
}

services/AIService.ts

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,68 @@
1-
import ModelClient from "@azure-rest/ai-inference";
2-
import { AzureKeyCredential } from "@azure/core-auth";
3-
41
export class AIService {
5-
private client: any = null;
6-
private readonly endpoint = "https://models.inference.ai.azure.com";
2+
private readonly endpoint = "https://models.inference.ai.azure.com/chat/completions";
73
private readonly token: string;
84

95
constructor() {
106
this.token = process.env.GITHUB_AI_TOKEN || "";
11-
}
12-
13-
private getClient() {
14-
if (!this.client && this.token) {
15-
try {
16-
this.client = ModelClient(this.endpoint, new AzureKeyCredential(this.token));
17-
} catch (e: any) {
18-
console.error(`[AIService] Erro ao inicializar cliente: ${e.message}`);
19-
return null;
20-
}
21-
}
22-
return this.client;
7+
if (!this.token) console.warn("[AIService] ⚠️ Token GITHUB_AI_TOKEN não encontrado!");
238
}
249

2510
async analyzeFailure(errorMessage: string, domSnapshot: string): Promise<string> {
26-
if (!this.token) return "IA desativada: Token não configurado.";
11+
console.log("[AIService] 🚀 Iniciando análise via Fetch Nativo...");
12+
13+
if (!this.token) return "IA desativada: Token ausente.";
2714

28-
const client = this.getClient();
29-
if (!client) return "IA indisponível: Falha na inicialização.";
30-
31-
// 🎯 PROMPT BLINDADO: Exige formato estrito
3215
const systemPrompt = `
33-
Você é um especialista em Auto-Healing para Playwright (QA).
34-
Analise o erro e o DOM fornecido.
35-
36-
SEU OBJETIVO: Encontrar o seletor CSS correto para corrigir o teste.
16+
Você é uma IA de Self-Healing para automação de testes (Playwright).
17+
Objetivo: Consertar seletores quebrados.
3718
38-
REGRAS CRÍTICAS DE RESPOSTA:
39-
1. Se encontrar o elemento, responda APENAS o seletor dentro de crases. Exemplo: \`#login-button\`
40-
2. NÃO explique nada. NÃO dê contexto. APENAS O SELETOR.
41-
3. Se não encontrar, responda: \`null\`
19+
Regra:
20+
1. Analise o erro e o DOM.
21+
2. Retorne APENAS o seletor CSS corrigido dentro de crases. Ex: \`#novo-id\`
22+
3. Se não achar, responda: null
4223
`;
4324

4425
try {
45-
// Cortamos o DOM para não estourar o limite de tokens e focar no essencial
46-
const truncatedDom = domSnapshot.slice(0, 15000);
26+
const truncatedDom = domSnapshot.slice(0, 10000);
4727

48-
const response = await client.path("/chat/completions").post({
49-
body: {
28+
console.log(`[AIService] 📤 Enviando requisição para gpt-4o-mini...`);
29+
30+
// ⚡ REQUISIÇÃO NATIVA (Sem SDK pesado)
31+
const response = await fetch(this.endpoint, {
32+
method: "POST",
33+
headers: {
34+
"Content-Type": "application/json",
35+
"Authorization": `Bearer ${this.token}`
36+
},
37+
body: JSON.stringify({
5038
messages: [
5139
{ role: "system", content: systemPrompt },
5240
{ role: "user", content: `Erro: ${errorMessage}\n\nDOM:\n${truncatedDom}` }
5341
],
54-
model: "gpt-4o",
55-
temperature: 0.1 // Temperatura baixa = Mais precisão, menos criatividade
56-
}
42+
model: "gpt-4o-mini", // Modelo mais rápido e leve
43+
temperature: 0.1,
44+
max_tokens: 100 // Limita resposta para ser veloz
45+
})
5746
});
5847

59-
if (response.status !== "200") {
60-
console.error(`[AIService] Erro na API: ${response.status}`);
48+
if (!response.ok) {
49+
console.error(`[AIService] ❌ Erro API: ${response.status} - ${response.statusText}`);
50+
const errorText = await response.text();
51+
console.error(`[AIService] Detalhe: ${errorText}`);
6152
return "Erro na API da IA";
6253
}
6354

64-
const data = response.body as any;
65-
return data.choices?.[0]?.message?.content || "Sem resposta.";
55+
const data = await response.json() as any;
56+
const content = data.choices?.[0]?.message?.content;
57+
58+
console.log(`[AIService] 📥 Resposta recebida: ${content}`);
59+
return content || "Sem resposta.";
6660

6761
} catch (error: any) {
68-
console.error(`[AIService] Exception: ${error.message}`);
69-
return "Erro interno no serviço de IA.";
62+
console.error(`[AIService] 💥 Exception: ${error.message}`);
63+
// Se for erro de certificado (comum em empresa), avisa
64+
if (error.cause) console.error(`[AIService] Causa: ${error.cause}`);
65+
return `Erro interno: ${error.message}`;
7066
}
7167
}
7268
}

0 commit comments

Comments
 (0)