SalwaM commited on
Commit
43932ea
·
verified ·
1 Parent(s): bbb29be

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +577 -0
app.py ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from datetime import datetime
3
+ from groq import Groq
4
+
5
+ import traceback
6
+ import json
7
+
8
+ # --- 1. الحصول على مفتاح API ---
9
+ import os
10
+
11
+
12
+ # تهيئة المكونات
13
+
14
+ api_key_coder= os.environ.get('Chat_with_Your_Context')
15
+
16
+ # تهيئة المكونات
17
+
18
+
19
+ # --- 2. تعريف عميل Groq مباشرة (بدون LangChain) ---
20
+ class GroqLLM:
21
+ def __init__(self, api_key, model="meta-llama/llama-4-scout-17b-16e-instruct", temperature=0.1):
22
+ self.client = Groq(api_key=api_key)
23
+ self.model = model
24
+ self.temperature = temperature
25
+
26
+ def invoke(self, prompt):
27
+ try:
28
+ response = self.client.chat.completions.create(
29
+ model=self.model,
30
+ messages=[{"role": "user", "content": prompt}],
31
+ temperature=self.temperature,
32
+ max_tokens=2000
33
+ )
34
+ return response.choices[0].message.content
35
+ except Exception as e:
36
+ return f"Error: {str(e)}"
37
+
38
+ # --- 3. تفعيل LLM ---
39
+ llm = GroqLLM(api_key=api_key_coder)
40
+
41
+ # --- 4. تعريف Dummy Runner و DOM Matcher ---
42
+ class DummyRunner:
43
+ def run(self, test_script):
44
+ # محاكاة لنتائج الاختبارات
45
+ if "fail" in test_script.lower() or "assert false" in test_script.lower():
46
+ return {
47
+ "status": "failed",
48
+ "error": "AssertionError: Expected True but got False",
49
+ "logs": "Stack trace: line 10 in test_function",
50
+ "dom": "<button id='submit-btn' class='btn'>Submit</button>"
51
+ }
52
+ return {"status": "passed", "message": "All tests passed successfully"}
53
+
54
+ class DOMMatcher:
55
+ def find_similar(self, dom, failed_locator):
56
+ # محاكاة لإيجاد locator بديل
57
+ return "button#submit-btn", 0.92
58
+
59
+ runner = DummyRunner()
60
+ dom_matcher = DOMMatcher()
61
+
62
+ # --- 5. تعريف وظائف الأدوات (بدون LangChain) ---
63
+ def detect_failure(test_script):
64
+ """اكتشاف فشل الاختبار"""
65
+ result = runner.run(test_script)
66
+ return result
67
+
68
+ def analyze_root_cause(failure_data):
69
+ """تحليل سبب الفشل باستخدام LLM"""
70
+ error = failure_data.get("error", "Unknown")
71
+ logs = failure_data.get("logs", "")
72
+
73
+ prompt = f"""
74
+ Analyze this test failure:
75
+
76
+ Error: {error}
77
+ Logs: {logs}
78
+
79
+ Provide:
80
+ 1. Root cause analysis
81
+ 2. Suggested fix
82
+ """
83
+
84
+ analysis = llm.invoke(prompt)
85
+ return {"root_cause": analysis, "confidence": "high"}
86
+
87
+ def heal_locator(failure_data):
88
+ """محاولة إصلاح الـ locator"""
89
+ dom = failure_data.get("dom", "")
90
+ error = failure_data.get("error", "")
91
+
92
+ new_locator, score = dom_matcher.find_similar(dom, error)
93
+ return {"suggested_locator": new_locator, "confidence": score}
94
+
95
+ def update_script(script_content, old_locator, new_locator):
96
+ """تحديث السكربت بـ locator جديد"""
97
+ return script_content.replace(old_locator, new_locator)
98
+
99
+ def reexecute_test(test_script):
100
+ """إعادة تشغيل الاختبار"""
101
+ return runner.run(test_script)
102
+
103
+ def generate_report(data):
104
+ """توليد تقرير شامل"""
105
+ prompt = f"""
106
+ Generate a comprehensive QA report based on this data:
107
+ {json.dumps(data, indent=2)}
108
+
109
+ Include:
110
+ - Test Execution Summary
111
+ - Failures Detected
112
+ - Root Cause Analysis
113
+ - Healing Actions
114
+ - Final Results
115
+ - Recommendations
116
+ """
117
+
118
+ return llm.invoke(prompt)
119
+
120
+ # --- 6. الدالة الرئيسية التي تنفذ كل الخطوات ---
121
+ def run_complete_analysis(test_script):
122
+ """تنفيذ تحليل كامل للاختبار"""
123
+
124
+ report_data = {
125
+ "original_script": test_script,
126
+ "steps": [],
127
+ "final_result": {},
128
+ "healing_applied": False
129
+ }
130
+
131
+ # الخطوة 1: تشغيل الاختبار
132
+ result = detect_failure(test_script)
133
+ report_data["steps"].append({"step": "initial_execution", "result": result})
134
+
135
+ # إذا فشل الاختبار
136
+ if result["status"] == "failed":
137
+ report_data["healing_applied"] = True
138
+
139
+ # الخطوة 2: تحليل السبب
140
+ analysis = analyze_root_cause(result)
141
+ report_data["steps"].append({"step": "root_cause_analysis", "analysis": analysis})
142
+
143
+ # الخطوة 3: محاولة الإصلاح
144
+ healing = heal_locator(result)
145
+ report_data["steps"].append({"step": "healing_attempt", "healing": healing})
146
+
147
+ # الخطوة 4: تحديث السكربت
148
+ if "suggested_locator" in healing:
149
+ old = "button"
150
+ new = healing["suggested_locator"]
151
+ updated_script = update_script(test_script, old, new)
152
+ report_data["steps"].append({"step": "script_updated", "new_script": updated_script})
153
+
154
+ # الخطوة 5: إعادة التشغيل
155
+ final_result = reexecute_test(updated_script)
156
+ report_data["final_result"] = final_result
157
+ report_data["steps"].append({"step": "re_execution", "result": final_result})
158
+ else:
159
+ report_data["final_result"] = result
160
+
161
+ # الخطوة 6: توليد التقرير
162
+ report = generate_report(report_data)
163
+ report_data["full_report"] = report
164
+
165
+ return report_data
166
+
167
+ # --- 7. دالة Gradio ---
168
+ def process_test_file(uploaded_file):
169
+ try:
170
+ if uploaded_file is None:
171
+ return "⛔ Please upload a Python test file.", None
172
+
173
+ # قراءة الملف
174
+ file_path = uploaded_file if isinstance(uploaded_file, str) else uploaded_file.name
175
+ with open(file_path, "r", encoding="utf-8") as f:
176
+ script_content = f.read()
177
+
178
+ # تنفيذ التحليل
179
+ result = run_complete_analysis(script_content)
180
+
181
+ # تجهيز العرض
182
+ display = f"""
183
+ # 📊 HealTest AI Analysis Result
184
+
185
+ ## 📁 Original Script:
186
+ ```python
187
+ {script_content[:500]}{'...' if len(script_content) > 500 else ''}
188
+ ```
189
+
190
+ ## 🔍 Analysis Steps:
191
+ """
192
+
193
+ for step in result["steps"]:
194
+ display += f"\n### ➡️ {step['step']}\n"
195
+ display += f"```\n{step}\n```\n"
196
+
197
+ display += f"\n## 📈 Final Result: {result['final_result']}\n"
198
+ display += f"\n## 📝 Full Report:\n{result['full_report']}\n"
199
+
200
+ # حفظ التقرير
201
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
202
+ report_file = f"healtest_report_{timestamp}.txt"
203
+
204
+ with open(report_file, "w", encoding="utf-8") as f:
205
+ f.write(f"HEALTEST AI REPORT\n{'='*50}\n\n")
206
+ f.write(f"Date: {datetime.now()}\n\n")
207
+ f.write(f"Original Script:\n{script_content}\n\n")
208
+ f.write(f"Analysis Result:\n{json.dumps(result, indent=2, ensure_ascii=False)}\n")
209
+
210
+ return display, report_file
211
+
212
+ except Exception as e:
213
+ error_trace = traceback.format_exc()
214
+ return f"❌ Error: {str(e)}\n\nDetails:\n{error_trace}", None
215
+
216
+ # SmartQA Full System with Multi-Source Test Generation + HealTest Integration
217
+ # Uses existing GroqLLM instance: llm = GroqLLM(api_key=api_key_coder)
218
+
219
+ # ==============================
220
+ # 1. Knowledge Input Definition
221
+ # ==============================
222
+
223
+ class KnowledgeInput:
224
+ def __init__(
225
+ self,
226
+ requirements=None,
227
+ dom=None,
228
+ api_spec=None,
229
+ user_flows=None,
230
+ source_code=None,
231
+ recording=None
232
+ ):
233
+ self.requirements = requirements
234
+ self.dom = dom
235
+ self.api_spec = api_spec
236
+ self.user_flows = user_flows
237
+ self.source_code = source_code
238
+ self.recording = recording
239
+
240
+
241
+ # ==============================
242
+ # 2. Knowledge Processor
243
+ # ==============================
244
+
245
+ class KnowledgeProcessor:
246
+
247
+ def parse_requirements(self, text):
248
+ return text.strip()
249
+
250
+ def parse_dom(self, dom_text):
251
+ return dom_text[:4000]
252
+
253
+ def parse_api(self, api_text):
254
+ return api_text[:4000]
255
+
256
+ def parse_flows(self, flows_text):
257
+ return flows_text.strip()
258
+
259
+ def analyze_code(self, code_text):
260
+ return code_text[:4000]
261
+
262
+ def parse_recording(self, rec_text):
263
+ return rec_text.strip()
264
+
265
+ def process(self, knowledge: KnowledgeInput):
266
+ data = {}
267
+
268
+ if knowledge.requirements:
269
+ data["req"] = self.parse_requirements(knowledge.requirements)
270
+
271
+ if knowledge.dom:
272
+ data["ui"] = self.parse_dom(knowledge.dom)
273
+
274
+ if knowledge.api_spec:
275
+ data["api"] = self.parse_api(knowledge.api_spec)
276
+
277
+ if knowledge.user_flows:
278
+ data["flows"] = self.parse_flows(knowledge.user_flows)
279
+
280
+ if knowledge.source_code:
281
+ data["code"] = self.analyze_code(knowledge.source_code)
282
+
283
+ if knowledge.recording:
284
+ data["record"] = self.parse_recording(knowledge.recording)
285
+
286
+ return data
287
+
288
+
289
+ # ==============================
290
+ # 3. Test Generator (LLM-based)
291
+ # ==============================
292
+
293
+ class TestGenerator:
294
+ def __init__(self, llm):
295
+ self.llm = llm
296
+
297
+ def generate_req_tests(self, data):
298
+ prompt = f"""
299
+ Generate Python Selenium automated test scripts from requirements.
300
+ Requirements:\n{data['req']}
301
+
302
+ Include:
303
+ - pytest format
304
+ - locators placeholders
305
+ - assertions
306
+ """
307
+ return self.llm.invoke(prompt)
308
+
309
+ def generate_ui_tests(self, data):
310
+ prompt = f"""
311
+ Generate Selenium UI tests from HTML DOM.
312
+ DOM:\n{data['ui']}
313
+ """
314
+ return self.llm.invoke(prompt)
315
+
316
+ def generate_api_tests(self, data):
317
+ prompt = f"""
318
+ Generate Python API tests using requests from OpenAPI/Swagger spec.
319
+ Spec:\n{data['api']}
320
+ """
321
+ return self.llm.invoke(prompt)
322
+
323
+ def generate_flow_tests(self, data):
324
+ prompt = f"""
325
+ Generate end-to-end Selenium tests from user flows.
326
+ Flows:\n{data['flows']}
327
+ """
328
+ return self.llm.invoke(prompt)
329
+
330
+ def generate_code_tests(self, data):
331
+ prompt = f"""
332
+ Analyze source code and generate relevant automated tests.
333
+ Code:\n{data['code']}
334
+ """
335
+ return self.llm.invoke(prompt)
336
+
337
+ def generate_record_tests(self, data):
338
+ prompt = f"""
339
+ Convert user interaction recording into Selenium test script.
340
+ Recording:\n{data['record']}
341
+ """
342
+ return self.llm.invoke(prompt)
343
+
344
+ def generate(self, processed_data):
345
+
346
+ if "api" in processed_data:
347
+ return self.generate_api_tests(processed_data)
348
+
349
+ if "ui" in processed_data:
350
+ return self.generate_ui_tests(processed_data)
351
+
352
+ if "flows" in processed_data:
353
+ return self.generate_flow_tests(processed_data)
354
+
355
+ if "req" in processed_data:
356
+ return self.generate_req_tests(processed_data)
357
+
358
+ if "code" in processed_data:
359
+ return self.generate_code_tests(processed_data)
360
+
361
+ if "record" in processed_data:
362
+ return self.generate_record_tests(processed_data)
363
+
364
+ return "No valid input provided"
365
+
366
+
367
+ # ==============================
368
+ # 4. HealTest Engine (Wrapper)
369
+ # ==============================
370
+
371
+ class HealTestEngine:
372
+ def __init__(self):
373
+ pass
374
+
375
+ def run_complete_analysis(self, test_script):
376
+ # Uses existing functions from your notebook
377
+ result = detect_failure(test_script)
378
+
379
+ if result["status"] == "failed":
380
+ analysis = analyze_root_cause(result)
381
+ healed = heal_locator(result)
382
+ updated_script = update_script(
383
+ test_script,
384
+ result.get("failed_locator", "old_locator"),
385
+ healed.get("new_locator", "new_locator")
386
+ )
387
+ re_result = reexecute_test(updated_script)
388
+ else:
389
+ analysis = "No failure"
390
+ healed = {}
391
+ updated_script = test_script
392
+ re_result = result
393
+
394
+ report_data = {
395
+ "original": test_script,
396
+ "analysis": analysis,
397
+ "healing": healed,
398
+ "final_result": re_result
399
+ }
400
+
401
+ report = generate_report(report_data)
402
+
403
+ return {
404
+ "generated_test": test_script,
405
+ "updated_test": updated_script,
406
+ "initial_result": result,
407
+ "final_result": re_result,
408
+ "report": report
409
+ }
410
+
411
+
412
+ # ==============================
413
+ # 5. SmartQA System
414
+ # ==============================
415
+
416
+ class SmartQASystem:
417
+ def __init__(self, llm):
418
+ self.processor = KnowledgeProcessor()
419
+ self.generator = TestGenerator(llm)
420
+ self.healer = HealTestEngine()
421
+
422
+ def run(self, knowledge: KnowledgeInput):
423
+ processed = self.processor.process(knowledge)
424
+ generated_tests = self.generator.generate(processed)
425
+ results = self.healer.run_complete_analysis(generated_tests)
426
+ return results
427
+
428
+
429
+ # ==============================
430
+ # 6. Gradio Interface
431
+ # ==============================
432
+ import gradio as gr
433
+
434
+ # --- 3. تفعيل LLM ---
435
+ llm = GroqLLM(api_key=api_key_coder)
436
+ system = SmartQASystem(llm)
437
+
438
+
439
+ def run_smartqa(requirements, dom, api_spec, flows, code, recording):
440
+ knowledge = KnowledgeInput(
441
+ requirements=requirements,
442
+ dom=dom,
443
+ api_spec=api_spec,
444
+ user_flows=flows,
445
+ source_code=code,
446
+ recording=recording
447
+ )
448
+
449
+ result = system.run(knowledge)
450
+
451
+ return (
452
+ result["generated_test"],
453
+ result["updated_test"],
454
+ str(result["initial_result"]),
455
+ str(result["final_result"]),
456
+ result["report"]
457
+ )
458
+
459
+
460
+ with gr.Blocks() as demo:
461
+ gr.Markdown("# 🧠 SmartQA — Multi-Source AI Test Generation & Self-Healing")
462
+ gr.Markdown("Provide any knowledge source to generate and heal automated tests")
463
+
464
+ with gr.Tab("Requirements"):
465
+ req_input = gr.Textbox(lines=8, label="Requirements")
466
+
467
+ with gr.Tab("UI / DOM"):
468
+ dom_input = gr.Textbox(lines=12, label="HTML DOM")
469
+
470
+ with gr.Tab("API Spec"):
471
+ api_input = gr.Textbox(lines=12, label="OpenAPI / Swagger")
472
+
473
+ with gr.Tab("User Flows"):
474
+ flow_input = gr.Textbox(lines=8, label="User Flows")
475
+
476
+ with gr.Tab("Source Code"):
477
+ code_input = gr.Textbox(lines=12, label="Source Code")
478
+
479
+ with gr.Tab("Recording"):
480
+ rec_input = gr.Textbox(lines=8, label="Interaction Recording")
481
+
482
+ # --- صف الأزرار أفقيًا ---
483
+ with gr.Row():
484
+ run_btn = gr.Button("🚀 Generate & Heal Tests", variant="primary")
485
+ example_btn = gr.Button("📂 Load Example Data")
486
+
487
+ # --- النتائج ---
488
+ gr.Markdown("## Results")
489
+ gen_out = gr.Code(label="Generated Test")
490
+ upd_out = gr.Code(label="Healed Test")
491
+ init_out = gr.Textbox(label="Initial Execution Result")
492
+ final_out = gr.Textbox(label="Final Execution Result")
493
+ report_out = gr.Textbox(lines=12, label="QA Report")
494
+
495
+ # --- بيانات أمثلة ---
496
+ example_requirements = """
497
+ User can login with email and password
498
+ User can search for a product
499
+ User can add product to cart
500
+ """
501
+
502
+ example_dom = """
503
+ <html>
504
+ <body>
505
+ <input id=\"email\" />
506
+ <input id=\"password\" />
507
+ <button id=\"login-btn\">Login</button>
508
+
509
+ <input id=\"search\" />
510
+ <button id=\"search-btn\">Search</button>
511
+
512
+ <button id=\"add-cart\">Add to Cart</button>
513
+ </body>
514
+ </html>
515
+ """
516
+
517
+ example_api = """
518
+ POST /login
519
+ Body: { email, password }
520
+
521
+ GET /products
522
+
523
+ POST /cart
524
+ Body: { product_id }
525
+ """
526
+
527
+ example_flows = """
528
+ Open login page
529
+ Enter email and password
530
+ Click login
531
+ Search product
532
+ Add to cart
533
+ """
534
+
535
+ example_code = """
536
+ @app.route('/login', methods=['POST'])
537
+ def login():
538
+ email = request.json['email']
539
+ password = request.json['password']
540
+ if authenticate(email, password):
541
+ return {'status': 'ok'}
542
+ return {'status': 'fail'}, 401
543
+ """
544
+
545
+ example_recording = """
546
+ User navigates to /login
547
+ Types email test@mail.com
548
+ Types password 123456
549
+ Clicks Login button
550
+ Navigates to /products
551
+ Clicks Add to Cart
552
+ """
553
+
554
+ def load_examples():
555
+ return (
556
+ example_requirements,
557
+ example_dom,
558
+ example_api,
559
+ example_flows,
560
+ example_code,
561
+ example_recording
562
+ )
563
+
564
+ # --- ربط الأزرار ---
565
+ run_btn.click(
566
+ fn=run_smartqa,
567
+ inputs=[req_input, dom_input, api_input, flow_input, code_input, rec_input],
568
+ outputs=[gen_out, upd_out, init_out, final_out, report_out]
569
+ )
570
+
571
+ example_btn.click(
572
+ fn=load_examples,
573
+ inputs=[],
574
+ outputs=[req_input, dom_input, api_input, flow_input, code_input, rec_input]
575
+ )
576
+
577
+ demo.launch(debug=True)