mmmay0722 commited on
Commit
ef3f08c
·
1 Parent(s): 51a1a1d

feat: enhance test case management and update README

Browse files

- Add automatic case indexing (1: CASE_NAME format) to fix duplicate names
- Add HuggingFace Spaces and community links to README

README.md CHANGED
@@ -1,7 +1,7 @@
1
- # WebQA Agent
2
 
3
  <!-- badges -->
4
- <p align="left">
5
  <a href="https://github.com/MigoXLab/webqa-agent/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MigoXLab/webqa-agent" alt="License"></a>
6
  <a href="https://github.com/MigoXLab/webqa-agent/stargazers"><img src="https://img.shields.io/github/stars/MigoXLab/webqa-agent" alt="GitHub stars"></a>
7
  <a href="https://github.com/MigoXLab/webqa-agent/network/members"><img src="https://img.shields.io/github/forks/MigoXLab/webqa-agent" alt="GitHub forks"></a>
@@ -9,9 +9,14 @@
9
  <a href="https://deepwiki.com/MigoXLab/webqa-agent"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
10
  </p>
11
 
12
- [English](README.md) · [简体中文](README_zh-CN.md)
 
 
 
 
 
13
 
14
- **WebQA Agent** is an autonomous web agent that audits performance, functionality, and UX for any web product.
15
 
16
  ## 🚀 Core Features
17
 
@@ -101,7 +106,9 @@ python webqa-agent.py
101
 
102
  ## Online Demo
103
 
104
- Experience online: [WebQA-Agent on ModelScope](https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary)
 
 
105
 
106
  ## Usage
107
 
 
1
+ <h1 align="center">WebQA Agent</h1>
2
 
3
  <!-- badges -->
4
+ <p align="center">
5
  <a href="https://github.com/MigoXLab/webqa-agent/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MigoXLab/webqa-agent" alt="License"></a>
6
  <a href="https://github.com/MigoXLab/webqa-agent/stargazers"><img src="https://img.shields.io/github/stars/MigoXLab/webqa-agent" alt="GitHub stars"></a>
7
  <a href="https://github.com/MigoXLab/webqa-agent/network/members"><img src="https://img.shields.io/github/forks/MigoXLab/webqa-agent" alt="GitHub forks"></a>
 
9
  <a href="https://deepwiki.com/MigoXLab/webqa-agent"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
10
  </p>
11
 
12
+ <p align="center">
13
+ Try Demo 🤗<a href="https://huggingface.co/spaces/mmmay0722/WebQA-Agent">HuggingFace</a> | 🚀<a href="https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary">ModelScope</a><br>
14
+ Join us on 🎮<a href="https://discord.gg/K5TtkVcx">Discord</a> | 💬<a href="https://aicarrier.feishu.cn/docx/NRNXdIirXoSQEHxhaqjchUfenzd">WeChat</a>
15
+ </p>
16
+
17
+ <p align="center"><a href="README.md">English</a> · <a href="README_zh-CN.md">简体中文</a></p>
18
 
19
+ <p align="center">🤖 <strong>WebQA Agent</strong> is an autonomous web browser agent that audits performance, functionality & UX for engineers and vibe-coding creators. ✨</p>
20
 
21
  ## 🚀 Core Features
22
 
 
106
 
107
  ## Online Demo
108
 
109
+ 🚀 **Try WebQA Agent Online:**
110
+ - **Hugging Face Spaces**: [WebQA-Agent on Hugging Face](https://huggingface.co/spaces/mmmay0722/WebQA-Agent)
111
+ - **ModelScope Studio**: [WebQA-Agent on ModelScope](https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary)
112
 
113
  ## Usage
114
 
README_zh-CN.md CHANGED
@@ -1,7 +1,7 @@
1
- # WebQA Agent
2
 
3
  <!-- badges -->
4
- <p align="left">
5
  <a href="https://github.com/MigoXLab/webqa-agent/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MigoXLab/webqa-agent" alt="License"></a>
6
  <a href="https://github.com/MigoXLab/webqa-agent/stargazers"><img src="https://img.shields.io/github/stars/MigoXLab/webqa-agent" alt="GitHub stars"></a>
7
  <a href="https://github.com/MigoXLab/webqa-agent/network/members"><img src="https://img.shields.io/github/forks/MigoXLab/webqa-agent" alt="GitHub forks"></a>
@@ -9,9 +9,14 @@
9
  <a href="https://deepwiki.com/MigoXLab/webqa-agent"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
10
  </p>
11
 
12
- [English](README.md) · [简体中文](README_zh-CN.md)
 
 
 
 
 
13
 
14
- **WebQA Agent** 是全自动网页评估测试 Agent,一键诊断性能、安全、功能与交互体验
15
 
16
  ## 🚀 核心特性
17
 
@@ -104,7 +109,9 @@ python webqa-agent.py
104
 
105
  ## 在线演示
106
 
107
- 进入ModelScope体验:[WebQA-Agent on ModelScope](https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary)
 
 
108
 
109
  ## 使用说明
110
 
 
1
+ <h1 align="center">WebQA Agent</h1>
2
 
3
  <!-- badges -->
4
+ <p align="center">
5
  <a href="https://github.com/MigoXLab/webqa-agent/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MigoXLab/webqa-agent" alt="License"></a>
6
  <a href="https://github.com/MigoXLab/webqa-agent/stargazers"><img src="https://img.shields.io/github/stars/MigoXLab/webqa-agent" alt="GitHub stars"></a>
7
  <a href="https://github.com/MigoXLab/webqa-agent/network/members"><img src="https://img.shields.io/github/forks/MigoXLab/webqa-agent" alt="GitHub forks"></a>
 
9
  <a href="https://deepwiki.com/MigoXLab/webqa-agent"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
10
  </p>
11
 
12
+ <p align="center">
13
+ 体验Demo 🤗<a href="https://huggingface.co/spaces/mmmay0722/WebQA-Agent">HuggingFace</a> | 🚀<a href="https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary">ModelScope</a><br>
14
+ 加入我们 🎮<a href="https://discord.gg/K5TtkVcx">Discord</a> | 💬<a href="https://aicarrier.feishu.cn/docx/NRNXdIirXoSQEHxhaqjchUfenzd">微信群</a>
15
+ </p>
16
+
17
+ <p align="center"><a href="README.md">English</a> · <a href="README_zh-CN.md">简体中文</a></p>
18
 
19
+ <p align="center">🤖 <strong>WebQA Agent</strong> 是全自动网页评估测试 Agent,一键完成性能、功能与交互体验的测试评估 ✨</p>
20
 
21
  ## 🚀 核心特性
22
 
 
109
 
110
  ## 在线演示
111
 
112
+ 🚀 **在线体验 WebQA Agent:**
113
+ - **Hugging Face Spaces**: [WebQA-Agent on Hugging Face](https://huggingface.co/spaces/mmmay0722/WebQA-Agent)
114
+ - **ModelScope Studio**: [WebQA-Agent on ModelScope](https://modelscope.cn/studios/mmmmei22/WebQA-Agent/summary)
115
 
116
  ## 使用说明
117
 
app_gradio/demo_gradio.py CHANGED
@@ -157,6 +157,16 @@ def create_config_dict(
157
  report_language: str = "zh-CN"
158
  ) -> Dict[str, Any]:
159
  """Create configuration dictionary"""
 
 
 
 
 
 
 
 
 
 
160
  config = {
161
  "target": {
162
  "url": url,
@@ -166,7 +176,7 @@ def create_config_dict(
166
  "function_test": {
167
  "enabled": function_test_enabled,
168
  "type": function_test_type,
169
- "business_objectives": business_objectives
170
  },
171
  "ux_test": {
172
  "enabled": ux_test_enabled
@@ -321,10 +331,6 @@ def submit_test(
321
  if not any([function_test_enabled, ux_test_enabled, performance_test_enabled, security_test_enabled]):
322
  return get_text(interface_language, "messages.error_no_tests"), "", False
323
 
324
- # If function test is enabled but no business objectives set
325
- if function_test_enabled and function_test_type == "ai" and not business_objectives.strip():
326
- return get_text(interface_language, "messages.error_no_business_objectives"), "", False
327
-
328
  # Validate LLM configuration
329
  valid, msg = validate_llm_config(api_key, base_url, model, interface_language)
330
  if not valid:
@@ -357,6 +363,7 @@ def submit_test(
357
  "tests": {
358
  "function": function_test_enabled,
359
  "function_type": function_test_type,
 
360
  "ux": ux_test_enabled,
361
  },
362
  "submitted_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
@@ -630,6 +637,15 @@ def create_gradio_interface(language: str = "zh-CN"):
630
  .fixed-width-table td:nth-child(6),
631
  .content-wrapper .gradio-dataframe th:nth-child(6),
632
  .content-wrapper .gradio-dataframe td:nth-child(6) {
 
 
 
 
 
 
 
 
 
633
  width: auto !important;
634
  max-width: none !important;
635
  min-width: 70px !important;
@@ -866,12 +882,21 @@ def create_gradio_interface(language: str = "zh-CN"):
866
  def get_history_rows(lang):
867
  rows = []
868
  for item in reversed(submission_history[-100:]):
 
 
 
 
 
 
 
 
869
  rows.append([
870
  item["submitted_at"],
871
  item["task_id"],
872
  item["url"],
873
  "✅" if item["tests"]["function"] else "-",
874
  item["tests"]["function_type"],
 
875
  "✅" if item["tests"]["ux"] else "-"
876
  ])
877
  return rows
 
157
  report_language: str = "zh-CN"
158
  ) -> Dict[str, Any]:
159
  """Create configuration dictionary"""
160
+
161
+ final_business_objectives = business_objectives.strip()
162
+ default_constraint = get_text(report_language, "config.default_business_objectives")
163
+
164
+ if final_business_objectives:
165
+ separator = ","
166
+ final_business_objectives = f"{final_business_objectives}{separator}{default_constraint}"
167
+ else:
168
+ final_business_objectives = default_constraint
169
+
170
  config = {
171
  "target": {
172
  "url": url,
 
176
  "function_test": {
177
  "enabled": function_test_enabled,
178
  "type": function_test_type,
179
+ "business_objectives": final_business_objectives
180
  },
181
  "ux_test": {
182
  "enabled": ux_test_enabled
 
331
  if not any([function_test_enabled, ux_test_enabled, performance_test_enabled, security_test_enabled]):
332
  return get_text(interface_language, "messages.error_no_tests"), "", False
333
 
 
 
 
 
334
  # Validate LLM configuration
335
  valid, msg = validate_llm_config(api_key, base_url, model, interface_language)
336
  if not valid:
 
363
  "tests": {
364
  "function": function_test_enabled,
365
  "function_type": function_test_type,
366
+ "business_objectives": business_objectives,
367
  "ux": ux_test_enabled,
368
  },
369
  "submitted_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
 
637
  .fixed-width-table td:nth-child(6),
638
  .content-wrapper .gradio-dataframe th:nth-child(6),
639
  .content-wrapper .gradio-dataframe td:nth-child(6) {
640
+ width: auto !important;
641
+ max-width: none !important;
642
+ min-width: 200px !important;
643
+ text-align: left !important;
644
+ }
645
+ .fixed-width-table th:nth-child(7),
646
+ .fixed-width-table td:nth-child(7),
647
+ .content-wrapper .gradio-dataframe th:nth-child(7),
648
+ .content-wrapper .gradio-dataframe td:nth-child(7) {
649
  width: auto !important;
650
  max-width: none !important;
651
  min-width: 70px !important;
 
882
  def get_history_rows(lang):
883
  rows = []
884
  for item in reversed(submission_history[-100:]):
885
+ business_objectives = item["tests"].get("business_objectives", "")
886
+ function_type = item["tests"]["function_type"]
887
+
888
+ if function_type == "ai" and business_objectives:
889
+ business_display = business_objectives[:30] + "..." if len(business_objectives) > 30 else business_objectives
890
+ else:
891
+ business_display = "-"
892
+
893
  rows.append([
894
  item["submitted_at"],
895
  item["task_id"],
896
  item["url"],
897
  "✅" if item["tests"]["function"] else "-",
898
  item["tests"]["function_type"],
899
+ business_display,
900
  "✅" if item["tests"]["ux"] else "-"
901
  ])
902
  return rows
app_gradio/gradio_i18n.json CHANGED
@@ -29,8 +29,9 @@
29
  "function_test_type": "功能测试类型",
30
  "function_test_type_info": "default: 遍历测试,覆盖可点击元素和所有链接\n ai: 基于视觉模型的智能测试,能够模拟真实用户行为、理解业务上下文,验证网页功能。",
31
  "business_objectives": "AI功能测试业务目标",
32
- "business_objectives_placeholder": "测试对话功能,生成2个用例",
33
- "business_objectives_info": "ai: 定制不同场景,精准发现复杂功能问题",
 
34
  "ux_test": "用户体验测试",
35
  "performance_test": "性能测试",
36
  "performance_test_info": "目前在 ModelScope 版本不可用;请前往 GitHub 体验",
@@ -54,7 +55,7 @@
54
  },
55
  "history": {
56
  "title": "提交记录",
57
- "headers": ["提交时间", "任务ID", "URL", "功能测试", "类型", "UX测试"],
58
  "refresh_btn": "🔄 刷新历史记录"
59
  },
60
  "messages": {
@@ -118,8 +119,9 @@
118
  "function_test_type": "Function Test Type",
119
  "function_test_type_info": "default: Traverse clickable elements & links.\n ai: Vision-model intelligent test simulating users & validating functionality.",
120
  "business_objectives": "AI Function Test Business Objectives",
121
- "business_objectives_placeholder": "Test chat functionality, generate 2 test cases",
122
- "business_objectives_info": "ai: Customize different scenarios, accurately find complex functional issues",
 
123
  "ux_test": "User Experience Test",
124
  "performance_test": "Performance Test",
125
  "performance_test_info": "Currently unavailable in HuggingFace version; please visit GitHub for experience",
@@ -143,7 +145,7 @@
143
  },
144
  "history": {
145
  "title": "Submission Records",
146
- "headers": ["Submit Time", "Task ID", "URL", "Function Test", "Type", "UX Test"],
147
  "refresh_btn": "🔄 Refresh History"
148
  },
149
  "messages": {
 
29
  "function_test_type": "功能测试类型",
30
  "function_test_type_info": "default: 遍历测试,覆盖可点击元素和所有链接\n ai: 基于视觉模型的智能测试,能够模拟真实用户行为、理解业务上下文,验证网页功能。",
31
  "business_objectives": "AI功能测试业务目标",
32
+ "business_objectives_placeholder": "测试对话功能",
33
+ "business_objectives_info": "ai: 定制不同场景,精准发现复杂功能问题。留空将使用默认设置(生成1个测试用例,每个用例包含6个步骤以内)",
34
+ "default_business_objectives": "生成1个测试用例,每个用例包含6个步骤以内",
35
  "ux_test": "用户体验测试",
36
  "performance_test": "性能测试",
37
  "performance_test_info": "目前在 ModelScope 版本不可用;请前往 GitHub 体验",
 
55
  },
56
  "history": {
57
  "title": "提交记录",
58
+ "headers": ["提交时间", "任务ID", "URL", "功能测试", "类型", "业务目标", "UX测试"],
59
  "refresh_btn": "🔄 刷新历史记录"
60
  },
61
  "messages": {
 
119
  "function_test_type": "Function Test Type",
120
  "function_test_type_info": "default: Traverse clickable elements & links.\n ai: Vision-model intelligent test simulating users & validating functionality.",
121
  "business_objectives": "AI Function Test Business Objectives",
122
+ "business_objectives_placeholder": "Test chat functionality",
123
+ "business_objectives_info": "ai: Customize different scenarios, accurately find complex functional issues. Leave blank to use default settings (generate 1 test case with no more than 6 steps per case)",
124
+ "default_business_objectives": "Generate 1 test case with no more than 6 steps per case",
125
  "ux_test": "User Experience Test",
126
  "performance_test": "Performance Test",
127
  "performance_test_info": "Currently unavailable in HuggingFace version; please visit GitHub for experience",
 
145
  },
146
  "history": {
147
  "title": "Submission Records",
148
+ "headers": ["Submit Time", "Task ID", "URL", "Function Test", "Type", "Business Objectives", "UX Test"],
149
  "refresh_btn": "🔄 Refresh History"
150
  },
151
  "messages": {
docs/images/webqa.svg CHANGED
webqa_agent/testers/function_tester.py CHANGED
@@ -335,6 +335,7 @@ class UITester:
335
  raise ValueError(f"Invalid JSON response: {str(je)}")
336
 
337
  if not plan_json.get("actions"):
 
338
  raise ValueError("No valid actions found in plan")
339
 
340
  return plan_json
@@ -477,8 +478,14 @@ class UITester:
477
  )
478
  self.finish_case("interrupted", "Case was interrupted by new case start")
479
 
 
 
 
 
480
  self.current_case_data = {
481
- "name": case_name,
 
 
482
  "start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
483
  "case_info": case_data or {},
484
  "steps": [],
@@ -494,7 +501,7 @@ class UITester:
494
  }
495
  self.current_case_steps = []
496
  self.step_counter = 0 # Reset step counter
497
- logging.debug(f"Started tracking case: {case_name} (step counter reset)")
498
 
499
  def add_step_data(self, step_data: Dict[str, Any], step_type: str = "action"):
500
  """Add step data to current case."""
@@ -547,6 +554,7 @@ class UITester:
547
  return
548
 
549
  case_name = self.current_case_data.get("name", "Unknown")
 
550
  steps_count = len(self.current_case_steps)
551
 
552
  # Get monitoring data
@@ -624,7 +632,7 @@ class UITester:
624
  total_steps = 0
625
  for i, case in enumerate(self.all_cases_data):
626
  case_steps = case.get("steps", [])
627
- case_name = case.get("name", f"Case_{i}")
628
  total_steps += len(case_steps)
629
  logging.debug(
630
  f"Report validation - Case '{case_name}': {len(case_steps)} steps, status: {case.get('status', 'unknown')}"
 
335
  raise ValueError(f"Invalid JSON response: {str(je)}")
336
 
337
  if not plan_json.get("actions"):
338
+ logging.error(f"No valid actions found in plan: {test_plan}")
339
  raise ValueError("No valid actions found in plan")
340
 
341
  return plan_json
 
478
  )
479
  self.finish_case("interrupted", "Case was interrupted by new case start")
480
 
481
+ # Calculate case index (1-based)
482
+ case_index = len(self.all_cases_data) + 1
483
+ formatted_case_name = f"{case_index}: {case_name}"
484
+
485
  self.current_case_data = {
486
+ "name": formatted_case_name,
487
+ "original_name": case_name, # Keep original name for reference
488
+ "case_index": case_index,
489
  "start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
490
  "case_info": case_data or {},
491
  "steps": [],
 
501
  }
502
  self.current_case_steps = []
503
  self.step_counter = 0 # Reset step counter
504
+ logging.debug(f"Started tracking case: {formatted_case_name} (step counter reset)")
505
 
506
  def add_step_data(self, step_data: Dict[str, Any], step_type: str = "action"):
507
  """Add step data to current case."""
 
554
  return
555
 
556
  case_name = self.current_case_data.get("name", "Unknown")
557
+ original_name = self.current_case_data.get("original_name", case_name)
558
  steps_count = len(self.current_case_steps)
559
 
560
  # Get monitoring data
 
632
  total_steps = 0
633
  for i, case in enumerate(self.all_cases_data):
634
  case_steps = case.get("steps", [])
635
+ case_name = case.get("name", f"Case_{i + 1}") # Use 1-based indexing as fallback
636
  total_steps += len(case_steps)
637
  logging.debug(
638
  f"Report validation - Case '{case_name}': {len(case_steps)} steps, status: {case.get('status', 'unknown')}"