rodski 5.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rodski-5.7.1/MANIFEST.in +19 -0
- rodski-5.7.1/PKG-INFO +101 -0
- rodski-5.7.1/README.md +93 -0
- rodski-5.7.1/pyproject.toml +98 -0
- rodski-5.7.1/rodski/LICENSE +21 -0
- rodski-5.7.1/rodski/README.md +56 -0
- rodski-5.7.1/rodski/__init__.py +2 -0
- rodski-5.7.1/rodski/api/__init__.py +0 -0
- rodski-5.7.1/rodski/api/rest_helper.py +116 -0
- rodski-5.7.1/rodski/cli_main.py +106 -0
- rodski-5.7.1/rodski/config/__init__.py +0 -0
- rodski-5.7.1/rodski/config/android_config.json +8 -0
- rodski-5.7.1/rodski/config/config.json +25 -0
- rodski-5.7.1/rodski/config/default_config.yaml +96 -0
- rodski-5.7.1/rodski/config/ios_config.json +7 -0
- rodski-5.7.1/rodski/config/llm_config.yaml +58 -0
- rodski-5.7.1/rodski/config/reviewer_config.yaml +31 -0
- rodski-5.7.1/rodski/config/vision_config.yaml +14 -0
- rodski-5.7.1/rodski/core/__init__.py +36 -0
- rodski-5.7.1/rodski/core/assertion/__init__.py +16 -0
- rodski-5.7.1/rodski/core/assertion/base_assertion.py +35 -0
- rodski-5.7.1/rodski/core/assertion/image_matcher.py +266 -0
- rodski-5.7.1/rodski/core/assertion/video_analyzer.py +271 -0
- rodski-5.7.1/rodski/core/browser_recycler.py +251 -0
- rodski-5.7.1/rodski/core/case_metadata.py +104 -0
- rodski-5.7.1/rodski/core/case_parser.py +159 -0
- rodski-5.7.1/rodski/core/condition_evaluator.py +92 -0
- rodski-5.7.1/rodski/core/config_manager.py +95 -0
- rodski-5.7.1/rodski/core/data_table_parser.py +93 -0
- rodski-5.7.1/rodski/core/diagnosis_engine.py +216 -0
- rodski-5.7.1/rodski/core/driver_factory.py +470 -0
- rodski-5.7.1/rodski/core/dynamic_executor.py +283 -0
- rodski-5.7.1/rodski/core/dynamic_steps.py +73 -0
- rodski-5.7.1/rodski/core/exceptions.py +408 -0
- rodski-5.7.1/rodski/core/execution_stats.py +81 -0
- rodski-5.7.1/rodski/core/global_value_parser.py +52 -0
- rodski-5.7.1/rodski/core/json_formatter.py +73 -0
- rodski-5.7.1/rodski/core/keyword_engine.py +2040 -0
- rodski-5.7.1/rodski/core/logger.py +81 -0
- rodski-5.7.1/rodski/core/loop_executor.py +202 -0
- rodski-5.7.1/rodski/core/metadata_writer.py +63 -0
- rodski-5.7.1/rodski/core/model_parser.py +306 -0
- rodski-5.7.1/rodski/core/parallel_executor.py +92 -0
- rodski-5.7.1/rodski/core/performance.py +48 -0
- rodski-5.7.1/rodski/core/profiler.py +85 -0
- rodski-5.7.1/rodski/core/recording/__init__.py +12 -0
- rodski-5.7.1/rodski/core/recording/recorder.py +142 -0
- rodski-5.7.1/rodski/core/recovery_engine.py +287 -0
- rodski-5.7.1/rodski/core/result_writer.py +227 -0
- rodski-5.7.1/rodski/core/runtime_context.py +20 -0
- rodski-5.7.1/rodski/core/runtime_control.py +115 -0
- rodski-5.7.1/rodski/core/ski_executor.py +652 -0
- rodski-5.7.1/rodski/core/statistics_collector.py +240 -0
- rodski-5.7.1/rodski/core/task_executor.py +122 -0
- rodski-5.7.1/rodski/core/test_case_explainer.py +1032 -0
- rodski-5.7.1/rodski/core/test_runner.py +269 -0
- rodski-5.7.1/rodski/core/xml_schema_validator.py +179 -0
- rodski-5.7.1/rodski/data/__init__.py +5 -0
- rodski-5.7.1/rodski/data/data_resolver.py +144 -0
- rodski-5.7.1/rodski/data/model_manager.py +90 -0
- rodski-5.7.1/rodski/drivers/__init__.py +39 -0
- rodski-5.7.1/rodski/drivers/android_driver.py +33 -0
- rodski-5.7.1/rodski/drivers/appium_driver.py +272 -0
- rodski-5.7.1/rodski/drivers/base_driver.py +356 -0
- rodski-5.7.1/rodski/drivers/desktop_driver.py +692 -0
- rodski-5.7.1/rodski/drivers/ios_driver.py +24 -0
- rodski-5.7.1/rodski/drivers/playwright_driver.py +629 -0
- rodski-5.7.1/rodski/drivers/pywinauto_driver.py +79 -0
- rodski-5.7.1/rodski/llm/__init__.py +10 -0
- rodski-5.7.1/rodski/llm/capabilities/__init__.py +23 -0
- rodski-5.7.1/rodski/llm/capabilities/base.py +14 -0
- rodski-5.7.1/rodski/llm/capabilities/screenshot_verifier.py +78 -0
- rodski-5.7.1/rodski/llm/capabilities/test_reviewer.py +131 -0
- rodski-5.7.1/rodski/llm/capabilities/vision_locator.py +62 -0
- rodski-5.7.1/rodski/llm/client.py +66 -0
- rodski-5.7.1/rodski/llm/config.py +134 -0
- rodski-5.7.1/rodski/llm/exceptions.py +21 -0
- rodski-5.7.1/rodski/llm/providers/__init__.py +17 -0
- rodski-5.7.1/rodski/llm/providers/base.py +38 -0
- rodski-5.7.1/rodski/llm/providers/claude.py +78 -0
- rodski-5.7.1/rodski/llm/providers/openai.py +73 -0
- rodski-5.7.1/rodski/reviewers/__init__.py +10 -0
- rodski-5.7.1/rodski/reviewers/cli.py +35 -0
- rodski-5.7.1/rodski/reviewers/llm_reviewer.py +149 -0
- rodski-5.7.1/rodski/rodski_cli/__init__.py +43 -0
- rodski-5.7.1/rodski/rodski_cli/config.py +50 -0
- rodski-5.7.1/rodski/rodski_cli/docs.py +126 -0
- rodski-5.7.1/rodski/rodski_cli/explain.py +129 -0
- rodski-5.7.1/rodski/rodski_cli/log.py +58 -0
- rodski-5.7.1/rodski/rodski_cli/model.py +57 -0
- rodski-5.7.1/rodski/rodski_cli/profile.py +57 -0
- rodski-5.7.1/rodski/rodski_cli/report.py +620 -0
- rodski-5.7.1/rodski/rodski_cli/run.py +229 -0
- rodski-5.7.1/rodski/rodski_cli/stats.py +305 -0
- rodski-5.7.1/rodski/schemas/case.xsd +128 -0
- rodski-5.7.1/rodski/schemas/data.xsd +56 -0
- rodski-5.7.1/rodski/schemas/globalvalue.xsd +48 -0
- rodski-5.7.1/rodski/schemas/model.xsd +177 -0
- rodski-5.7.1/rodski/schemas/result.xsd +104 -0
- rodski-5.7.1/rodski/ski_gui.py +6 -0
- rodski-5.7.1/rodski/ski_run.py +132 -0
- rodski-5.7.1/rodski/ui/__init__.py +0 -0
- rodski-5.7.1/rodski/utils/__init__.py +4 -0
- rodski-5.7.1/rodski/utils/helpers.py +29 -0
- rodski-5.7.1/rodski/vision/__init__.py +67 -0
- rodski-5.7.1/rodski/vision/ai_verifier.py +378 -0
- rodski-5.7.1/rodski/vision/bbox_locator.py +147 -0
- rodski-5.7.1/rodski/vision/cache.py +253 -0
- rodski-5.7.1/rodski/vision/coordinate_utils.py +315 -0
- rodski-5.7.1/rodski/vision/desktop_driver.py +258 -0
- rodski-5.7.1/rodski/vision/exceptions.py +267 -0
- rodski-5.7.1/rodski/vision/llm_analyzer.py +92 -0
- rodski-5.7.1/rodski/vision/locator.py +575 -0
- rodski-5.7.1/rodski/vision/matcher.py +136 -0
- rodski-5.7.1/rodski/vision/ocr_locator.py +333 -0
- rodski-5.7.1/rodski/vision/omni_client.py +220 -0
- rodski-5.7.1/rodski/vision/screen_recorder.py +210 -0
- rodski-5.7.1/rodski/vision/screenshot.py +148 -0
- rodski-5.7.1/rodski.egg-info/PKG-INFO +101 -0
- rodski-5.7.1/rodski.egg-info/SOURCES.txt +123 -0
- rodski-5.7.1/rodski.egg-info/dependency_links.txt +1 -0
- rodski-5.7.1/rodski.egg-info/entry_points.txt +2 -0
- rodski-5.7.1/rodski.egg-info/requires.txt +29 -0
- rodski-5.7.1/rodski.egg-info/top_level.txt +1 -0
- rodski-5.7.1/setup.cfg +4 -0
rodski-5.7.1/MANIFEST.in
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
include rodski/LICENSE
|
|
2
|
+
include rodski/README.md
|
|
3
|
+
|
|
4
|
+
recursive-include rodski/schemas *.xsd
|
|
5
|
+
recursive-include rodski/config *.yaml *.json
|
|
6
|
+
|
|
7
|
+
prune rodski/tests
|
|
8
|
+
prune rodski/docs
|
|
9
|
+
prune rodski/examples
|
|
10
|
+
prune rodski/scripts
|
|
11
|
+
prune rodski/images
|
|
12
|
+
prune rodski/rodski_docs
|
|
13
|
+
|
|
14
|
+
exclude rodski/.flake8
|
|
15
|
+
exclude rodski/bandit.yaml
|
|
16
|
+
exclude rodski/mypy.ini
|
|
17
|
+
exclude rodski/pytest.ini
|
|
18
|
+
exclude rodski/selftest.py
|
|
19
|
+
exclude rodski/requirements.txt
|
rodski-5.7.1/PKG-INFO
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rodski
|
|
3
|
+
Version: 5.7.1
|
|
4
|
+
Summary: RodSki - XML-driven keyword automation testing framework for AI agents
|
|
5
|
+
Author: Lightning Strike Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/user/RodSki
|
|
8
|
+
Project-URL: Repository, https://github.com/user/RodSki
|
|
9
|
+
Keywords: testing,automation,keyword-driven,xml,playwright,appium,ai-agent,test-framework
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: xmlschema>=2.5.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
25
|
+
Requires-Dist: tqdm>=4.65.0
|
|
26
|
+
Requires-Dist: psutil>=5.9.0
|
|
27
|
+
Requires-Dist: requests>=2.31.0
|
|
28
|
+
Provides-Extra: web
|
|
29
|
+
Requires-Dist: playwright>=1.40.0; extra == "web"
|
|
30
|
+
Provides-Extra: mobile
|
|
31
|
+
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "mobile"
|
|
32
|
+
Provides-Extra: gui
|
|
33
|
+
Requires-Dist: PyQt6>=6.6.0; extra == "gui"
|
|
34
|
+
Provides-Extra: vision
|
|
35
|
+
Requires-Dist: pillow>=10.0.0; extra == "vision"
|
|
36
|
+
Provides-Extra: llm
|
|
37
|
+
Requires-Dist: anthropic>=0.18.0; extra == "llm"
|
|
38
|
+
Requires-Dist: openai>=1.12.0; extra == "llm"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
|
43
|
+
Provides-Extra: all
|
|
44
|
+
Requires-Dist: rodski[gui,llm,mobile,vision,web]; extra == "all"
|
|
45
|
+
|
|
46
|
+
# RodSki - 自动化测试框架
|
|
47
|
+
|
|
48
|
+
基于关键字驱动的现代化自动化测试框架,Python 重写版本。
|
|
49
|
+
|
|
50
|
+
## 特性
|
|
51
|
+
|
|
52
|
+
- 🎯 关键字驱动 - 简单易用的测试语法
|
|
53
|
+
- 🔧 模型驱动 - 模型与数据分离
|
|
54
|
+
- 🌐 多驱动支持 - Playwright (Web) + PyWinAuto (Desktop) + Appium (Mobile)
|
|
55
|
+
- 📊 XML 用例 - 结构化测试定义
|
|
56
|
+
- 🔌 RESTful API - 接口测试支持
|
|
57
|
+
|
|
58
|
+
## 快速开始
|
|
59
|
+
|
|
60
|
+
📖 **[5分钟快速入门指南](docs/user-guides/QUICKSTART.md)** - 新手必读!
|
|
61
|
+
|
|
62
|
+
### 安装
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install -r requirements.txt
|
|
66
|
+
playwright install chromium
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### CLI 运行
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python3 cli_main.py run examples/product/DEMO/demo_site/case/demo_case.xml
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 项目结构
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
rodski/
|
|
79
|
+
├── core/ # 核心引擎
|
|
80
|
+
├── drivers/ # 驱动层
|
|
81
|
+
├── api/ # API 测试
|
|
82
|
+
├── config/ # 配置
|
|
83
|
+
├── data/ # 测试数据
|
|
84
|
+
├── examples/ # 示例用例
|
|
85
|
+
└── docs/ # 文档
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 开发
|
|
89
|
+
|
|
90
|
+
### 环境要求
|
|
91
|
+
|
|
92
|
+
- Python 3.8+
|
|
93
|
+
- Playwright
|
|
94
|
+
|
|
95
|
+
### 贡献
|
|
96
|
+
|
|
97
|
+
欢迎提交 Issue 和 Pull Request!
|
|
98
|
+
|
|
99
|
+
## 许可证
|
|
100
|
+
|
|
101
|
+
MIT License
|
rodski-5.7.1/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# RodSki
|
|
2
|
+
|
|
3
|
+
**RodSki** 是面向 AI Agent 的跨平台确定性测试执行引擎,基于 XML 活文档协议,支持 Web(Playwright)、Android(Appium)、iOS(Appium)和桌面应用(PyWinAuto)的自动化测试执行。
|
|
4
|
+
|
|
5
|
+
> Agent 负责思考,RodSki 负责稳定执行。
|
|
6
|
+
|
|
7
|
+
## 特性
|
|
8
|
+
|
|
9
|
+
- **结构化 XML 协议** — model / case / data 活文档,Agent 可读可写
|
|
10
|
+
- **多平台确定性执行** — Web / Android / iOS / Desktop 统一关键字
|
|
11
|
+
- **视觉定位能力** — OmniParser + LLM 语义定位(可选 AI 能力层)
|
|
12
|
+
- **Agent 友好 CLI** — run / validate / explain / dry-run,JSON 结构化输出
|
|
13
|
+
- **活文档模式** — Agent 写 XML → RodSki 执行 → 结果反馈 → Agent 分析
|
|
14
|
+
- **智能等待** — 自动处理元素加载延迟,零配置开箱即用
|
|
15
|
+
- **智能诊断** — AI 辅助失败分析与恢复建议(可选)
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 安装
|
|
21
|
+
pip install -r requirements.txt
|
|
22
|
+
|
|
23
|
+
# 执行测试
|
|
24
|
+
rodski run case/ --output-format json
|
|
25
|
+
|
|
26
|
+
# 解释用例(自然语言)
|
|
27
|
+
rodski explain case/login.xml
|
|
28
|
+
|
|
29
|
+
# 干跑模式(仅验证不执行)
|
|
30
|
+
rodski run case/ --dry-run
|
|
31
|
+
|
|
32
|
+
# 详细输出
|
|
33
|
+
rodski run case/ --verbose
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 架构概览
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Agent (探索/决策) → XML (活文档) → RodSki (执行) → JSON (结果) → Agent (分析)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
RodSki 作为 Agent 工具链中的执行层,提供确定性、可重复的测试执行能力。Agent 通过 XML 协议描述测试意图,RodSki 负责跨平台执行并以 JSON 格式返回结构化结果,供 Agent 进一步分析和决策。
|
|
43
|
+
|
|
44
|
+
## 项目结构
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
RodSki/
|
|
48
|
+
├── rodski/ # 核心框架
|
|
49
|
+
│ ├── core/ # 执行引擎(关键字、解析器、诊断)
|
|
50
|
+
│ ├── drivers/ # 平台驱动(Playwright/Appium/PyWinAuto)
|
|
51
|
+
│ ├── llm/ # LLM 能力层(可选)
|
|
52
|
+
│ ├── vision/ # 视觉定位(可选)
|
|
53
|
+
│ ├── rodski_cli/ # CLI 子命令
|
|
54
|
+
│ ├── config/ # 配置文件
|
|
55
|
+
│ └── docs/ # 框架文档
|
|
56
|
+
├── rodski-demo/ # 官方示例
|
|
57
|
+
└── .pb/ # 项目管理文档
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 文档
|
|
61
|
+
|
|
62
|
+
| 文档 | 说明 |
|
|
63
|
+
|------|------|
|
|
64
|
+
| [Agent 集成指南](rodski/docs/AGENT_INTEGRATION.md) | Agent 接入主入口 |
|
|
65
|
+
| [用例编写指南](rodski/docs/TEST_CASE_WRITING_GUIDE.md) | XML 用例编写规范 |
|
|
66
|
+
| [核心设计约束](rodski/docs/CORE_DESIGN_CONSTRAINTS.md) | 框架不可违反的设计约束 |
|
|
67
|
+
| [关键字参考](rodski/docs/SKILL_REFERENCE.md) | 全部关键字语法说明 |
|
|
68
|
+
| [架构说明](rodski/docs/ARCHITECTURE.md) | 框架内部架构 |
|
|
69
|
+
| [视觉定位](rodski/docs/VISION_LOCATION.md) | OmniParser 视觉定位能力 |
|
|
70
|
+
|
|
71
|
+
## 智能等待
|
|
72
|
+
|
|
73
|
+
RodSki 内置智能等待机制,自动处理 UI 元素的加载延迟,无需手动添加等待步骤。
|
|
74
|
+
|
|
75
|
+
- **零配置** — 默认启用,开箱即用
|
|
76
|
+
- **性能优化** — 元素就绪时立即执行,不浪费时间
|
|
77
|
+
- **自动重试** — 元素未加载时自动重试(默认 30 次 x 300ms = 9 秒)
|
|
78
|
+
- **可配置** — 支持自定义重试次数和间隔
|
|
79
|
+
|
|
80
|
+
配置项位于 `rodski/config/config.json`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"smart_wait_enabled": true,
|
|
85
|
+
"smart_wait_max_retries": 30,
|
|
86
|
+
"smart_wait_retry_interval": 0.3,
|
|
87
|
+
"smart_wait_log_retry": true
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
MIT License - 详见 [LICENSE](rodski/LICENSE)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "rodski"
|
|
7
|
+
version = "5.7.1"
|
|
8
|
+
description = "RodSki - XML-driven keyword automation testing framework for AI agents"
|
|
9
|
+
readme = {file = "rodski/README.md", content-type = "text/markdown"}
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Lightning Strike Team"},
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"testing", "automation", "keyword-driven", "xml", "playwright",
|
|
17
|
+
"appium", "ai-agent", "test-framework",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Software Development :: Testing",
|
|
30
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dependencies = [
|
|
34
|
+
"xmlschema>=2.5.0",
|
|
35
|
+
"pyyaml>=6.0.0",
|
|
36
|
+
"tqdm>=4.65.0",
|
|
37
|
+
"psutil>=5.9.0",
|
|
38
|
+
"requests>=2.31.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.optional-dependencies]
|
|
42
|
+
web = ["playwright>=1.40.0"]
|
|
43
|
+
mobile = ["Appium-Python-Client>=3.0.0"]
|
|
44
|
+
gui = ["PyQt6>=6.6.0"]
|
|
45
|
+
vision = ["pillow>=10.0.0"]
|
|
46
|
+
llm = ["anthropic>=0.18.0", "openai>=1.12.0"]
|
|
47
|
+
dev = ["pytest>=7.4.0", "pytest-cov>=4.1.0", "pytest-mock>=3.12.0"]
|
|
48
|
+
all = ["rodski[web,mobile,gui,vision,llm]"]
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
rodski = "rodski.cli_main:main"
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://github.com/user/RodSki"
|
|
55
|
+
Repository = "https://github.com/user/RodSki"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
include = ["rodski", "rodski.*"]
|
|
59
|
+
exclude = [
|
|
60
|
+
"rodski.tests", "rodski.tests.*",
|
|
61
|
+
"rodski.docs", "rodski.docs.*",
|
|
62
|
+
"rodski.examples", "rodski.examples.*",
|
|
63
|
+
"rodski.scripts", "rodski.scripts.*",
|
|
64
|
+
"rodski.images", "rodski.images.*",
|
|
65
|
+
"rodski.rodski_docs", "rodski.rodski_docs.*",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.setuptools.package-data]
|
|
69
|
+
rodski = [
|
|
70
|
+
"schemas/*.xsd",
|
|
71
|
+
"config/*.yaml",
|
|
72
|
+
"config/*.json",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.black]
|
|
76
|
+
line-length = 120
|
|
77
|
+
target-version = ['py39']
|
|
78
|
+
exclude = '''
|
|
79
|
+
/(
|
|
80
|
+
\.git
|
|
81
|
+
| \.venv
|
|
82
|
+
| venv
|
|
83
|
+
| build
|
|
84
|
+
| dist
|
|
85
|
+
)/
|
|
86
|
+
'''
|
|
87
|
+
|
|
88
|
+
[tool.isort]
|
|
89
|
+
profile = "black"
|
|
90
|
+
line_length = 120
|
|
91
|
+
|
|
92
|
+
[tool.coverage.run]
|
|
93
|
+
source = ["rodski"]
|
|
94
|
+
omit = ["*/tests/*", "*/demo_*/*"]
|
|
95
|
+
|
|
96
|
+
[tool.coverage.report]
|
|
97
|
+
fail_under = 80
|
|
98
|
+
precision = 2
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lightning Strike Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# RodSki - 自动化测试框架
|
|
2
|
+
|
|
3
|
+
基于关键字驱动的现代化自动化测试框架,Python 重写版本。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- 🎯 关键字驱动 - 简单易用的测试语法
|
|
8
|
+
- 🔧 模型驱动 - 模型与数据分离
|
|
9
|
+
- 🌐 多驱动支持 - Playwright (Web) + PyWinAuto (Desktop) + Appium (Mobile)
|
|
10
|
+
- 📊 XML 用例 - 结构化测试定义
|
|
11
|
+
- 🔌 RESTful API - 接口测试支持
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
📖 **[5分钟快速入门指南](docs/user-guides/QUICKSTART.md)** - 新手必读!
|
|
16
|
+
|
|
17
|
+
### 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install -r requirements.txt
|
|
21
|
+
playwright install chromium
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### CLI 运行
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python3 cli_main.py run examples/product/DEMO/demo_site/case/demo_case.xml
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 项目结构
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
rodski/
|
|
34
|
+
├── core/ # 核心引擎
|
|
35
|
+
├── drivers/ # 驱动层
|
|
36
|
+
├── api/ # API 测试
|
|
37
|
+
├── config/ # 配置
|
|
38
|
+
├── data/ # 测试数据
|
|
39
|
+
├── examples/ # 示例用例
|
|
40
|
+
└── docs/ # 文档
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 开发
|
|
44
|
+
|
|
45
|
+
### 环境要求
|
|
46
|
+
|
|
47
|
+
- Python 3.8+
|
|
48
|
+
- Playwright
|
|
49
|
+
|
|
50
|
+
### 贡献
|
|
51
|
+
|
|
52
|
+
欢迎提交 Issue 和 Pull Request!
|
|
53
|
+
|
|
54
|
+
## 许可证
|
|
55
|
+
|
|
56
|
+
MIT License
|
|
File without changes
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""RESTful API 测试辅助工具"""
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
from jsonschema import validate, ValidationError
|
|
6
|
+
import requests
|
|
7
|
+
from requests.adapters import HTTPAdapter
|
|
8
|
+
from urllib3.util.retry import Retry
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RestHelper:
|
|
14
|
+
"""REST API 测试辅助类"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _get_session(max_retries: int = 3, pool_connections: int = 10, pool_maxsize: int = 10) -> requests.Session:
|
|
18
|
+
"""获取配置了连接池和重试的 Session"""
|
|
19
|
+
session = requests.Session()
|
|
20
|
+
retry_strategy = Retry(
|
|
21
|
+
total=max_retries,
|
|
22
|
+
backoff_factor=0.3,
|
|
23
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
24
|
+
)
|
|
25
|
+
adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=pool_connections, pool_maxsize=pool_maxsize)
|
|
26
|
+
session.mount("http://", adapter)
|
|
27
|
+
session.mount("https://", adapter)
|
|
28
|
+
return session
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def validate_json_schema(data: Dict, schema: Dict) -> bool:
|
|
32
|
+
"""验证 JSON Schema"""
|
|
33
|
+
try:
|
|
34
|
+
validate(instance=data, schema=schema)
|
|
35
|
+
return True
|
|
36
|
+
except ValidationError:
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def jsonpath_extract(data: Dict, path: str) -> Any:
|
|
41
|
+
"""简单 JSONPath 提取(支持点号路径)"""
|
|
42
|
+
keys = path.strip('$.').split('.')
|
|
43
|
+
current = data
|
|
44
|
+
for key in keys:
|
|
45
|
+
if isinstance(current, dict):
|
|
46
|
+
current = current.get(key)
|
|
47
|
+
else:
|
|
48
|
+
return None
|
|
49
|
+
return current
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def send_request(
|
|
53
|
+
method: str,
|
|
54
|
+
url: str,
|
|
55
|
+
headers: Optional[Dict[str, str]] = None,
|
|
56
|
+
body: Any = None,
|
|
57
|
+
timeout: float = 30.0,
|
|
58
|
+
max_retries: int = 3,
|
|
59
|
+
use_session: bool = True,
|
|
60
|
+
cookies: Optional[Dict[str, str]] = None,
|
|
61
|
+
) -> requests.Response:
|
|
62
|
+
"""发送 HTTP 请求"""
|
|
63
|
+
method = method.upper()
|
|
64
|
+
kwargs: Dict[str, Any] = {"headers": headers, "timeout": timeout}
|
|
65
|
+
if body is not None:
|
|
66
|
+
kwargs["json"] = body
|
|
67
|
+
if cookies:
|
|
68
|
+
kwargs["cookies"] = cookies
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
logger.debug(f"Sending {method} request to {url}")
|
|
72
|
+
if use_session:
|
|
73
|
+
session = RestHelper._get_session(max_retries=max_retries)
|
|
74
|
+
response = session.request(method, url, **kwargs)
|
|
75
|
+
else:
|
|
76
|
+
response = requests.request(method, url, **kwargs)
|
|
77
|
+
logger.debug(f"Response: {response.status_code} in {response.elapsed.total_seconds():.3f}s")
|
|
78
|
+
return response
|
|
79
|
+
except requests.exceptions.Timeout as e:
|
|
80
|
+
logger.error(f"Request timeout: {url}")
|
|
81
|
+
raise
|
|
82
|
+
except requests.exceptions.ConnectionError as e:
|
|
83
|
+
logger.error(f"Connection error: {url}")
|
|
84
|
+
raise
|
|
85
|
+
except requests.exceptions.RequestException as e:
|
|
86
|
+
logger.error(f"Request failed: {url} - {str(e)}")
|
|
87
|
+
raise
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def assert_response_time(response: requests.Response, max_ms: float) -> bool:
|
|
91
|
+
"""断言响应时间(毫秒)"""
|
|
92
|
+
elapsed_ms = response.elapsed.total_seconds() * 1000
|
|
93
|
+
return elapsed_ms <= max_ms
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def extract_headers(response: requests.Response, header_name: str) -> Optional[str]:
|
|
97
|
+
"""提取响应头"""
|
|
98
|
+
return response.headers.get(header_name)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def compare_responses(
|
|
102
|
+
resp1: Dict,
|
|
103
|
+
resp2: Dict,
|
|
104
|
+
ignore_fields: Optional[List[str]] = None,
|
|
105
|
+
) -> bool:
|
|
106
|
+
"""比较两个响应(忽略指定字段)"""
|
|
107
|
+
ignore_fields = ignore_fields or []
|
|
108
|
+
|
|
109
|
+
def _strip(d: Any) -> Any:
|
|
110
|
+
if isinstance(d, dict):
|
|
111
|
+
return {k: _strip(v) for k, v in d.items() if k not in ignore_fields}
|
|
112
|
+
if isinstance(d, list):
|
|
113
|
+
return [_strip(i) for i in d]
|
|
114
|
+
return d
|
|
115
|
+
|
|
116
|
+
return _strip(resp1) == _strip(resp2)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""RodSki CLI 入口"""
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import traceback
|
|
6
|
+
from .rodski_cli import run, model, config, log, report, profile, docs
|
|
7
|
+
|
|
8
|
+
from rodski import __version__ as VERSION
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def format_error(e, verbose=False):
|
|
12
|
+
"""格式化错误信息,提高可读性"""
|
|
13
|
+
error_type = type(e).__name__
|
|
14
|
+
error_msg = str(e)
|
|
15
|
+
|
|
16
|
+
# 常见错误的友好提示
|
|
17
|
+
hints = {
|
|
18
|
+
FileNotFoundError: "请检查文件路径是否正确",
|
|
19
|
+
PermissionError: "请检查文件权限或以管理员身份运行",
|
|
20
|
+
ConnectionError: "请检查网络连接或目标服务是否可用",
|
|
21
|
+
TimeoutError: "操作超时,请检查网络或增加超时时间",
|
|
22
|
+
KeyboardInterrupt: "用户中断执行",
|
|
23
|
+
ImportError: "缺少依赖包,请运行: pip install -r requirements.txt",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
hint = hints.get(type(e), "")
|
|
27
|
+
lines = [f"错误 [{error_type}]: {error_msg}"]
|
|
28
|
+
if hint:
|
|
29
|
+
lines.append(f"提示: {hint}")
|
|
30
|
+
if verbose:
|
|
31
|
+
lines.append("")
|
|
32
|
+
lines.append("详细堆栈信息:")
|
|
33
|
+
lines.append(traceback.format_exc())
|
|
34
|
+
return "\n".join(lines)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main():
|
|
38
|
+
parser = argparse.ArgumentParser(
|
|
39
|
+
prog="rodski",
|
|
40
|
+
description="RodSki - 关键字驱动测试框架",
|
|
41
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
42
|
+
epilog="示例:\n"
|
|
43
|
+
" ski run case/ 执行测试用例\n"
|
|
44
|
+
" ski run case/ --dry-run 验证用例但不执行\n"
|
|
45
|
+
" ski run case/ --verbose 详细输出模式\n"
|
|
46
|
+
" ski run case/ --step-by-step 单步执行模式\n"
|
|
47
|
+
" ski run case/ --pause-on-error 错误时暂停\n"
|
|
48
|
+
" ski run case/ --interactive 交互模式\n"
|
|
49
|
+
" ski config list 查看配置\n"
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument("--version", action="version", version=f"RodSki {VERSION}")
|
|
52
|
+
parser.add_argument("--verbose", action="store_true", help="详细输出模式")
|
|
53
|
+
|
|
54
|
+
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
|
55
|
+
|
|
56
|
+
# run 子命令
|
|
57
|
+
run.setup_parser(subparsers)
|
|
58
|
+
|
|
59
|
+
# model 子命令
|
|
60
|
+
model.setup_parser(subparsers)
|
|
61
|
+
|
|
62
|
+
# config 子命令
|
|
63
|
+
config.setup_parser(subparsers)
|
|
64
|
+
|
|
65
|
+
# log 子命令
|
|
66
|
+
log.setup_parser(subparsers)
|
|
67
|
+
|
|
68
|
+
# report 子命令
|
|
69
|
+
report.setup_parser(subparsers)
|
|
70
|
+
|
|
71
|
+
# profile 子命令
|
|
72
|
+
profile.setup_parser(subparsers)
|
|
73
|
+
|
|
74
|
+
# docs 子命令
|
|
75
|
+
docs.setup_parser(subparsers)
|
|
76
|
+
|
|
77
|
+
args = parser.parse_args()
|
|
78
|
+
|
|
79
|
+
if not args.command:
|
|
80
|
+
parser.print_help()
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
# 执行对应子命令
|
|
84
|
+
handlers = {
|
|
85
|
+
"run": run.handle,
|
|
86
|
+
"model": model.handle,
|
|
87
|
+
"config": config.handle,
|
|
88
|
+
"log": log.handle,
|
|
89
|
+
"report": report.handle,
|
|
90
|
+
"profile": profile.handle,
|
|
91
|
+
"docs": docs.handle
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
verbose = getattr(args, "verbose", False)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
return handlers[args.command](args)
|
|
98
|
+
except KeyboardInterrupt:
|
|
99
|
+
print("\n操作已被用户中断", file=sys.stderr)
|
|
100
|
+
return 130
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(format_error(e, verbose=verbose), file=sys.stderr)
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
sys.exit(main())
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"driver": "web",
|
|
3
|
+
"browser": "chromium",
|
|
4
|
+
"headless": false,
|
|
5
|
+
"timeout": 60,
|
|
6
|
+
"retry": 0,
|
|
7
|
+
"retry_delay": 1.0,
|
|
8
|
+
"retry_on_errors": [
|
|
9
|
+
"ElementNotFound",
|
|
10
|
+
"Timeout",
|
|
11
|
+
"StaleElement"
|
|
12
|
+
],
|
|
13
|
+
"log_level": "INFO",
|
|
14
|
+
"log_dir": "logs",
|
|
15
|
+
"report_format": "html",
|
|
16
|
+
"auto_screenshot_on_failure": true,
|
|
17
|
+
"auto_screenshot_on_step": true,
|
|
18
|
+
"screenshot_dir": "screenshots",
|
|
19
|
+
"smart_wait_enabled": true,
|
|
20
|
+
"smart_wait_max_retries": 30,
|
|
21
|
+
"smart_wait_retry_interval": 0.3,
|
|
22
|
+
"smart_wait_log_retry": true,
|
|
23
|
+
"element_wait_timeout": 0.5,
|
|
24
|
+
"element_retry_interval": 0.1
|
|
25
|
+
}
|