kdtest-pw 2.0.0__py3-none-any.whl
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.
- kdtest_pw/__init__.py +50 -0
- kdtest_pw/action/__init__.py +7 -0
- kdtest_pw/action/base_keyword.py +292 -0
- kdtest_pw/action/element_plus/__init__.py +23 -0
- kdtest_pw/action/element_plus/el_cascader.py +263 -0
- kdtest_pw/action/element_plus/el_datepicker.py +324 -0
- kdtest_pw/action/element_plus/el_dialog.py +317 -0
- kdtest_pw/action/element_plus/el_form.py +443 -0
- kdtest_pw/action/element_plus/el_menu.py +456 -0
- kdtest_pw/action/element_plus/el_select.py +268 -0
- kdtest_pw/action/element_plus/el_table.py +442 -0
- kdtest_pw/action/element_plus/el_tree.py +364 -0
- kdtest_pw/action/element_plus/el_upload.py +313 -0
- kdtest_pw/action/key_retrieval.py +311 -0
- kdtest_pw/action/page_action.py +1129 -0
- kdtest_pw/api/__init__.py +6 -0
- kdtest_pw/api/api_keyword.py +251 -0
- kdtest_pw/api/request_handler.py +232 -0
- kdtest_pw/cases/__init__.py +6 -0
- kdtest_pw/cases/case_collector.py +182 -0
- kdtest_pw/cases/case_executor.py +359 -0
- kdtest_pw/cases/read/__init__.py +6 -0
- kdtest_pw/cases/read/cell_handler.py +305 -0
- kdtest_pw/cases/read/excel_reader.py +223 -0
- kdtest_pw/cli/__init__.py +5 -0
- kdtest_pw/cli/run.py +318 -0
- kdtest_pw/common.py +106 -0
- kdtest_pw/core/__init__.py +7 -0
- kdtest_pw/core/browser_manager.py +196 -0
- kdtest_pw/core/config_loader.py +235 -0
- kdtest_pw/core/page_context.py +228 -0
- kdtest_pw/data/__init__.py +5 -0
- kdtest_pw/data/init_data.py +105 -0
- kdtest_pw/data/static/elementData.yaml +59 -0
- kdtest_pw/data/static/parameters.json +24 -0
- kdtest_pw/plugins/__init__.py +6 -0
- kdtest_pw/plugins/element_plus_plugin/__init__.py +5 -0
- kdtest_pw/plugins/element_plus_plugin/elementData/elementData.yaml +144 -0
- kdtest_pw/plugins/element_plus_plugin/element_plus_plugin.py +237 -0
- kdtest_pw/plugins/element_plus_plugin/my.ini +23 -0
- kdtest_pw/plugins/plugin_base.py +180 -0
- kdtest_pw/plugins/plugin_loader.py +260 -0
- kdtest_pw/product.py +5 -0
- kdtest_pw/reference.py +99 -0
- kdtest_pw/utils/__init__.py +13 -0
- kdtest_pw/utils/built_in_function.py +376 -0
- kdtest_pw/utils/decorator.py +211 -0
- kdtest_pw/utils/log/__init__.py +6 -0
- kdtest_pw/utils/log/html_report.py +336 -0
- kdtest_pw/utils/log/logger.py +123 -0
- kdtest_pw/utils/public_script.py +366 -0
- kdtest_pw-2.0.0.dist-info/METADATA +169 -0
- kdtest_pw-2.0.0.dist-info/RECORD +57 -0
- kdtest_pw-2.0.0.dist-info/WHEEL +5 -0
- kdtest_pw-2.0.0.dist-info/entry_points.txt +2 -0
- kdtest_pw-2.0.0.dist-info/licenses/LICENSE +21 -0
- kdtest_pw-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"""公共逻辑方法"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import hashlib
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ..reference import INFO
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PublicScript:
|
|
13
|
+
"""公共脚本工具类
|
|
14
|
+
|
|
15
|
+
提供文件操作、数据处理等公共方法。
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# ==================== 文件操作 ====================
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def read_file(file_path: str, encoding: str = 'utf-8') -> str:
|
|
22
|
+
"""读取文件内容
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
file_path: 文件路径
|
|
26
|
+
encoding: 编码
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
str: 文件内容
|
|
30
|
+
"""
|
|
31
|
+
with open(file_path, 'r', encoding=encoding) as f:
|
|
32
|
+
return f.read()
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def write_file(file_path: str, content: str, encoding: str = 'utf-8') -> None:
|
|
36
|
+
"""写入文件
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
file_path: 文件路径
|
|
40
|
+
content: 内容
|
|
41
|
+
encoding: 编码
|
|
42
|
+
"""
|
|
43
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
with open(file_path, 'w', encoding=encoding) as f:
|
|
45
|
+
f.write(content)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def append_file(file_path: str, content: str, encoding: str = 'utf-8') -> None:
|
|
49
|
+
"""追加写入文件
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
file_path: 文件路径
|
|
53
|
+
content: 内容
|
|
54
|
+
encoding: 编码
|
|
55
|
+
"""
|
|
56
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
with open(file_path, 'a', encoding=encoding) as f:
|
|
58
|
+
f.write(content)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def read_json(file_path: str) -> Any:
|
|
62
|
+
"""读取 JSON 文件
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
file_path: 文件路径
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
解析后的 JSON 数据
|
|
69
|
+
"""
|
|
70
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
71
|
+
return json.load(f)
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def write_json(file_path: str, data: Any, indent: int = 2) -> None:
|
|
75
|
+
"""写入 JSON 文件
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
file_path: 文件路径
|
|
79
|
+
data: 数据
|
|
80
|
+
indent: 缩进
|
|
81
|
+
"""
|
|
82
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
84
|
+
json.dump(data, f, indent=indent, ensure_ascii=False)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def read_yaml(file_path: str) -> Any:
|
|
88
|
+
"""读取 YAML 文件
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
file_path: 文件路径
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
解析后的 YAML 数据
|
|
95
|
+
"""
|
|
96
|
+
import yaml
|
|
97
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
98
|
+
return yaml.safe_load(f)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def write_yaml(file_path: str, data: Any) -> None:
|
|
102
|
+
"""写入 YAML 文件
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
file_path: 文件路径
|
|
106
|
+
data: 数据
|
|
107
|
+
"""
|
|
108
|
+
import yaml
|
|
109
|
+
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
111
|
+
yaml.dump(data, f, default_flow_style=False, allow_unicode=True)
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def file_exists(file_path: str) -> bool:
|
|
115
|
+
"""检查文件是否存在
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
file_path: 文件路径
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
bool: 是否存在
|
|
122
|
+
"""
|
|
123
|
+
return Path(file_path).exists()
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def delete_file(file_path: str) -> bool:
|
|
127
|
+
"""删除文件
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
file_path: 文件路径
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
bool: 是否成功
|
|
134
|
+
"""
|
|
135
|
+
try:
|
|
136
|
+
Path(file_path).unlink(missing_ok=True)
|
|
137
|
+
return True
|
|
138
|
+
except Exception:
|
|
139
|
+
return False
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def list_files(directory: str, pattern: str = '*') -> List[str]:
|
|
143
|
+
"""列出目录下的文件
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
directory: 目录路径
|
|
147
|
+
pattern: 匹配模式
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
List[str]: 文件路径列表
|
|
151
|
+
"""
|
|
152
|
+
return [str(p) for p in Path(directory).glob(pattern)]
|
|
153
|
+
|
|
154
|
+
# ==================== 数据处理 ====================
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def dict_get(data: Dict, key_path: str, default: Any = None) -> Any:
|
|
158
|
+
"""从嵌套字典获取值
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
data: 字典
|
|
162
|
+
key_path: 键路径 (用点分隔)
|
|
163
|
+
default: 默认值
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
值
|
|
167
|
+
"""
|
|
168
|
+
keys = key_path.split('.')
|
|
169
|
+
value = data
|
|
170
|
+
|
|
171
|
+
for key in keys:
|
|
172
|
+
if isinstance(value, dict):
|
|
173
|
+
value = value.get(key)
|
|
174
|
+
elif isinstance(value, list) and key.isdigit():
|
|
175
|
+
index = int(key)
|
|
176
|
+
value = value[index] if 0 <= index < len(value) else None
|
|
177
|
+
else:
|
|
178
|
+
return default
|
|
179
|
+
|
|
180
|
+
if value is None:
|
|
181
|
+
return default
|
|
182
|
+
|
|
183
|
+
return value
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def dict_set(data: Dict, key_path: str, value: Any) -> None:
|
|
187
|
+
"""设置嵌套字典的值
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
data: 字典
|
|
191
|
+
key_path: 键路径
|
|
192
|
+
value: 值
|
|
193
|
+
"""
|
|
194
|
+
keys = key_path.split('.')
|
|
195
|
+
|
|
196
|
+
for key in keys[:-1]:
|
|
197
|
+
if key not in data:
|
|
198
|
+
data[key] = {}
|
|
199
|
+
data = data[key]
|
|
200
|
+
|
|
201
|
+
data[keys[-1]] = value
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def merge_dicts(*dicts: Dict) -> Dict:
|
|
205
|
+
"""合并多个字典
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
*dicts: 要合并的字典
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Dict: 合并后的字典
|
|
212
|
+
"""
|
|
213
|
+
result = {}
|
|
214
|
+
for d in dicts:
|
|
215
|
+
result.update(d)
|
|
216
|
+
return result
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def list_to_dict(data: List[Dict], key: str) -> Dict:
|
|
220
|
+
"""将列表转换为字典
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
data: 字典列表
|
|
224
|
+
key: 作为键的字段名
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Dict: 转换后的字典
|
|
228
|
+
"""
|
|
229
|
+
return {item[key]: item for item in data if key in item}
|
|
230
|
+
|
|
231
|
+
# ==================== 加密工具 ====================
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def md5(text: str) -> str:
|
|
235
|
+
"""MD5 加密
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
text: 原文
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
str: MD5 值
|
|
242
|
+
"""
|
|
243
|
+
return hashlib.md5(text.encode()).hexdigest()
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def sha256(text: str) -> str:
|
|
247
|
+
"""SHA256 加密
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
text: 原文
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
str: SHA256 值
|
|
254
|
+
"""
|
|
255
|
+
return hashlib.sha256(text.encode()).hexdigest()
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def base64_encode(text: str) -> str:
|
|
259
|
+
"""Base64 编码
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
text: 原文
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
str: 编码后的字符串
|
|
266
|
+
"""
|
|
267
|
+
import base64
|
|
268
|
+
return base64.b64encode(text.encode()).decode()
|
|
269
|
+
|
|
270
|
+
@staticmethod
|
|
271
|
+
def base64_decode(text: str) -> str:
|
|
272
|
+
"""Base64 解码
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
text: 编码字符串
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
str: 解码后的字符串
|
|
279
|
+
"""
|
|
280
|
+
import base64
|
|
281
|
+
return base64.b64decode(text.encode()).decode()
|
|
282
|
+
|
|
283
|
+
# ==================== 环境变量 ====================
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def get_env(name: str, default: str = '') -> str:
|
|
287
|
+
"""获取环境变量
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
name: 变量名
|
|
291
|
+
default: 默认值
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
str: 环境变量值
|
|
295
|
+
"""
|
|
296
|
+
return os.environ.get(name, default)
|
|
297
|
+
|
|
298
|
+
@staticmethod
|
|
299
|
+
def set_env(name: str, value: str) -> None:
|
|
300
|
+
"""设置环境变量
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
name: 变量名
|
|
304
|
+
value: 值
|
|
305
|
+
"""
|
|
306
|
+
os.environ[name] = value
|
|
307
|
+
|
|
308
|
+
# ==================== 验证工具 ====================
|
|
309
|
+
|
|
310
|
+
@staticmethod
|
|
311
|
+
def is_email(text: str) -> bool:
|
|
312
|
+
"""验证邮箱格式
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
text: 文本
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
bool: 是否有效
|
|
319
|
+
"""
|
|
320
|
+
import re
|
|
321
|
+
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
322
|
+
return bool(re.match(pattern, text))
|
|
323
|
+
|
|
324
|
+
@staticmethod
|
|
325
|
+
def is_phone(text: str) -> bool:
|
|
326
|
+
"""验证手机号格式
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
text: 文本
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
bool: 是否有效
|
|
333
|
+
"""
|
|
334
|
+
import re
|
|
335
|
+
pattern = r'^1[3-9]\d{9}$'
|
|
336
|
+
return bool(re.match(pattern, text))
|
|
337
|
+
|
|
338
|
+
@staticmethod
|
|
339
|
+
def is_url(text: str) -> bool:
|
|
340
|
+
"""验证 URL 格式
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
text: 文本
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
bool: 是否有效
|
|
347
|
+
"""
|
|
348
|
+
import re
|
|
349
|
+
pattern = r'^https?://[^\s/$.?#].[^\s]*$'
|
|
350
|
+
return bool(re.match(pattern, text, re.IGNORECASE))
|
|
351
|
+
|
|
352
|
+
@staticmethod
|
|
353
|
+
def is_json(text: str) -> bool:
|
|
354
|
+
"""验证 JSON 格式
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
text: 文本
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
bool: 是否有效
|
|
361
|
+
"""
|
|
362
|
+
try:
|
|
363
|
+
json.loads(text)
|
|
364
|
+
return True
|
|
365
|
+
except (json.JSONDecodeError, TypeError):
|
|
366
|
+
return False
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kdtest-pw
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: KDTest Playwright Edition - 关键字驱动测试框架,专为 Element Plus/Vue3 优化
|
|
5
|
+
Home-page: https://github.com/kdtest/kdtest-pw
|
|
6
|
+
Author: KDTest Team
|
|
7
|
+
Author-email: KDTest Team <kdtest@example.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/kdtest/kdtest-pw
|
|
10
|
+
Project-URL: Documentation, https://github.com/kdtest/kdtest-pw#readme
|
|
11
|
+
Project-URL: Repository, https://github.com/kdtest/kdtest-pw
|
|
12
|
+
Keywords: testing,automation,playwright,keyword-driven,element-plus,vue3
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: playwright>=1.40.0
|
|
28
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
29
|
+
Requires-Dist: PyYAML>=6.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-playwright>=0.4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build; extra == "dev"
|
|
34
|
+
Requires-Dist: twine; extra == "dev"
|
|
35
|
+
Dynamic: author
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
|
|
40
|
+
# KDTest-Playwright
|
|
41
|
+
|
|
42
|
+
基于 Playwright 的关键字驱动测试框架,专为 Element Plus/Vue3 优化。
|
|
43
|
+
|
|
44
|
+
## 安装
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# 从 PyPI 安装
|
|
48
|
+
pip install kdtest-pw
|
|
49
|
+
|
|
50
|
+
# 安装 Playwright 浏览器
|
|
51
|
+
playwright install chromium
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 快速开始
|
|
55
|
+
|
|
56
|
+
### 1. 创建配置文件 `parameters.json`
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"testCaseFile": [
|
|
61
|
+
{
|
|
62
|
+
"caseFilePath": "testCases.xlsx",
|
|
63
|
+
"caseItem": ["Sheet1"]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"browser": "chromium",
|
|
67
|
+
"url": "http://localhost:8080",
|
|
68
|
+
"headless": false,
|
|
69
|
+
"timeout": 30000
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. 创建 Excel 测试用例
|
|
74
|
+
|
|
75
|
+
| 用例ID | 用例名称 | 描述 | 关键字 | 定位信息 | 操作值 |
|
|
76
|
+
|--------|----------|------|--------|----------|--------|
|
|
77
|
+
| TC001 | 登录测试 | | | | |
|
|
78
|
+
| | | 打开网站 | driver_get | | http://localhost |
|
|
79
|
+
| | | 输入用户名 | input_text | xpath | //input[@placeholder='账号'] | admin |
|
|
80
|
+
| | | 点击登录 | click_btn | css | .el-button--primary | |
|
|
81
|
+
|
|
82
|
+
### 3. 运行测试
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 命令行运行
|
|
86
|
+
kdtest-pw -c parameters.json
|
|
87
|
+
|
|
88
|
+
# 或在 Python 中使用
|
|
89
|
+
from kdtest_pw.cli import KDTestRunner
|
|
90
|
+
|
|
91
|
+
runner = KDTestRunner("parameters.json")
|
|
92
|
+
runner.setup()
|
|
93
|
+
runner.run()
|
|
94
|
+
runner.teardown()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 核心关键字
|
|
98
|
+
|
|
99
|
+
### 浏览器操作
|
|
100
|
+
- `driver_get` - 导航到 URL
|
|
101
|
+
- `driver_back` - 后退
|
|
102
|
+
- `driver_refresh` - 刷新
|
|
103
|
+
- `driver_close` - 关闭页面
|
|
104
|
+
|
|
105
|
+
### 输入操作
|
|
106
|
+
- `input_text` - 输入文本(清除后输入)
|
|
107
|
+
- `input_append` - 追加输入
|
|
108
|
+
- `input_clear` - 清除输入
|
|
109
|
+
|
|
110
|
+
### 点击操作
|
|
111
|
+
- `click_btn` - 点击元素
|
|
112
|
+
- `double_click` - 双击
|
|
113
|
+
- `right_click` - 右键点击
|
|
114
|
+
- `hover` - 鼠标悬停
|
|
115
|
+
|
|
116
|
+
### 断言
|
|
117
|
+
- `text_assert` - 文本断言
|
|
118
|
+
- `title_assert` - 标题断言
|
|
119
|
+
- `element_visible_assert` - 可见性断言
|
|
120
|
+
- `element_count_assert` - 数量断言
|
|
121
|
+
|
|
122
|
+
### Element Plus 组件
|
|
123
|
+
- `el_select` - 下拉选择
|
|
124
|
+
- `el_datepicker` - 日期选择
|
|
125
|
+
- `el_dialog_confirm` - 对话框确认
|
|
126
|
+
- `el_table_click_row` - 表格行点击
|
|
127
|
+
- `el_tree_click_node` - 树节点点击
|
|
128
|
+
- `el_cascader` - 级联选择
|
|
129
|
+
- `el_upload` - 文件上传
|
|
130
|
+
- `el_form_input` - 表单输入
|
|
131
|
+
|
|
132
|
+
### 菜单导航
|
|
133
|
+
- `menu_load_data` - 加载菜单配置
|
|
134
|
+
- `menu_navigate` - 导航到菜单
|
|
135
|
+
- `menu_switch_iframe` - 切换 iframe
|
|
136
|
+
- `menu_exit_iframe` - 退出 iframe
|
|
137
|
+
|
|
138
|
+
## 变量和内置函数
|
|
139
|
+
|
|
140
|
+
### 变量引用
|
|
141
|
+
```
|
|
142
|
+
${varName} # 从缓存获取
|
|
143
|
+
${self.param} # 自定义参数
|
|
144
|
+
${env.NAME} # 环境变量
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 内置函数
|
|
148
|
+
```
|
|
149
|
+
@today() # 今天日期
|
|
150
|
+
@now() # 当前时间
|
|
151
|
+
@random(1,100) # 随机数
|
|
152
|
+
@random_phone() # 随机手机号
|
|
153
|
+
@uuid() # UUID
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 与 kdtest (Selenium) 的区别
|
|
157
|
+
|
|
158
|
+
| 特性 | kdtest (Selenium) | kdtest-pw (Playwright) |
|
|
159
|
+
|------|-------------------|------------------------|
|
|
160
|
+
| 浏览器驱动 | 需要下载 WebDriver | 内置浏览器 |
|
|
161
|
+
| 等待机制 | 显式/隐式等待 | 自动等待 |
|
|
162
|
+
| Element Plus | 需要额外处理 | 原生支持 |
|
|
163
|
+
| 多标签页 | 需要切换句柄 | 原生支持 |
|
|
164
|
+
| 录制回放 | 无 | 支持 Trace |
|
|
165
|
+
| 视频录制 | 无 | 原生支持 |
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
MIT
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
kdtest_pw/__init__.py,sha256=kbWVNaPicqC4yvMR51NBCZhklpaHxoRatkVrRAqQHBw,947
|
|
2
|
+
kdtest_pw/common.py,sha256=8ilBred-Vza2EgzRDmjapXmKry5cYTznZyZqUzqrEAQ,2732
|
|
3
|
+
kdtest_pw/product.py,sha256=0xyrdLjqK-Pphmgn-d8YXb0WxVlfNY97ovCcuJ4DmN4,163
|
|
4
|
+
kdtest_pw/reference.py,sha256=7kmQg6sB2pxO6RVEdRuRZ1RayoNx3KZ-mVdN1M_zTZY,2082
|
|
5
|
+
kdtest_pw/action/__init__.py,sha256=9zJa6vGbBq788b-FLOEg5gWhf1MNXUQHNICqRemY1po,211
|
|
6
|
+
kdtest_pw/action/base_keyword.py,sha256=hM2cPvHUcYrkb6tdlRy5NMTbSPbN3epK5wev4tRMJWM,9082
|
|
7
|
+
kdtest_pw/action/key_retrieval.py,sha256=L2lrkgiHB0YHh-oNvsy0AP0gQ04YP4O460EDiHN0K-E,10433
|
|
8
|
+
kdtest_pw/action/page_action.py,sha256=lkCg5nkMv-XyoKc3PQv4AukT0_tLlfWxR1s9ingFpOs,31268
|
|
9
|
+
kdtest_pw/action/element_plus/__init__.py,sha256=e9QwNNoYY2EYgo1N5Pt8NfxcqR0qXuoSNYBLdfslieQ,612
|
|
10
|
+
kdtest_pw/action/element_plus/el_cascader.py,sha256=RyEeVhGsd_JKcFbQQre6o38LOBBQsFvbYcyCoN4YTzU,8006
|
|
11
|
+
kdtest_pw/action/element_plus/el_datepicker.py,sha256=cUqU-Xy0bYxUTttQTf_qeM3V3z8P4vZM40ihrAQpafk,9730
|
|
12
|
+
kdtest_pw/action/element_plus/el_dialog.py,sha256=FhATmQnc0IMtoHk987uvGtdqdOYoTqhHBGXzgjqFxSc,10077
|
|
13
|
+
kdtest_pw/action/element_plus/el_form.py,sha256=aOMmmaZqAON79idvf2htfdqeR7iZhdPw-HYCSE655po,13132
|
|
14
|
+
kdtest_pw/action/element_plus/el_menu.py,sha256=0TorgHPz4SaDybFv0F7D2jM8OlY6nNgUhEd7TEQEp3s,14562
|
|
15
|
+
kdtest_pw/action/element_plus/el_select.py,sha256=oVF0_l3Q8PzUhKdpH9rgo957iQcNZUJRPO3WgQKBMe8,7830
|
|
16
|
+
kdtest_pw/action/element_plus/el_table.py,sha256=E8MF03iuj7niuxZofa2ePao3KiqK0BCcavbPt9QNCH0,12484
|
|
17
|
+
kdtest_pw/action/element_plus/el_tree.py,sha256=klWCTOOryKO1YZawOX9t0o5rtZLTlTg93mxlOgh1jH4,10916
|
|
18
|
+
kdtest_pw/action/element_plus/el_upload.py,sha256=hRUHDWHJI2FP-XI4G3emDTkfGOxwEc8vG8dOrmV0Ods,9252
|
|
19
|
+
kdtest_pw/api/__init__.py,sha256=lItPmMeRbsIX2LpUDx1PgMJscjfvKzmflQp_VPZHLxM,148
|
|
20
|
+
kdtest_pw/api/api_keyword.py,sha256=GTvPBhpy7BJMs7Pc3JXKB3X63pPQI2vrJA1fAkE6Nh0,6909
|
|
21
|
+
kdtest_pw/api/request_handler.py,sha256=XC2V_MjQ4QK82ccn7sqgM8ZwBRB_fVeqeWOTsgvklLw,6150
|
|
22
|
+
kdtest_pw/cases/__init__.py,sha256=Imn4Kb7qzLr3oTxVE2MV2IdxA-PA2oAgfD5mSRB5oEQ,153
|
|
23
|
+
kdtest_pw/cases/case_collector.py,sha256=rd5y8eZrslwQu2yiYFypP0UbKJDHFvjcm2VSfM6Cj4E,5332
|
|
24
|
+
kdtest_pw/cases/case_executor.py,sha256=SH71wJpcc1mV4Z_bSVCOMUb8_ZQ1jq_CuGhVTSRhPJU,10884
|
|
25
|
+
kdtest_pw/cases/read/__init__.py,sha256=J2b6AhGd-DLbvAf0BtUeufmjpZE10x4HUeudIZuJdLE,144
|
|
26
|
+
kdtest_pw/cases/read/cell_handler.py,sha256=qrXhG3w6o5rI0_LHrMYbdEC4_v8WFxRMbExhE_VmYMQ,8383
|
|
27
|
+
kdtest_pw/cases/read/excel_reader.py,sha256=23jC-eckCJ1gt99rv851ICwL5Ohexa483bWcJyER-Mw,6711
|
|
28
|
+
kdtest_pw/cli/__init__.py,sha256=-HKjQDPfpt2wnCIo3s5xtSkpUQI95XrdmQ9Kp84EO6A,90
|
|
29
|
+
kdtest_pw/cli/run.py,sha256=PpI84objdftklPshjv3EV71UudoVxQJfIBecCL-yKm0,8839
|
|
30
|
+
kdtest_pw/core/__init__.py,sha256=EDzLQl-77BuP_WQE4VkoWOLm79qZk-F3z-YSMWqrTqc,203
|
|
31
|
+
kdtest_pw/core/browser_manager.py,sha256=gqqgzH4heAtXraVmLlyYpBcR2S4opolcPgddkN_af6E,5867
|
|
32
|
+
kdtest_pw/core/config_loader.py,sha256=nOYR0R4sCAd5iT2sXNTtAbS1f7KHdLwzBJtbXr2og1A,6658
|
|
33
|
+
kdtest_pw/core/page_context.py,sha256=yVslWCupj2TeC_VFl6ZYxnsD5ZdONdxfBnYJjfPi2H4,6726
|
|
34
|
+
kdtest_pw/data/__init__.py,sha256=wWdyTFTj2fSrH1vQERYXPH6crLDfj-JpsfdNhTtgu7M,76
|
|
35
|
+
kdtest_pw/data/init_data.py,sha256=ta_Yf0MZzgKPIKz7PidBM9Z6nsYWy4RCKQVaqvFoi3o,3277
|
|
36
|
+
kdtest_pw/data/static/elementData.yaml,sha256=FP9T8iOJDdj5rxjS5-H84-B7_P5SxKfSKaCPlNOkB68,1073
|
|
37
|
+
kdtest_pw/data/static/parameters.json,sha256=m_VYzPbDcFpLJNiNMQxnxYW_XGpL0fZw-4z1aDoApdY,536
|
|
38
|
+
kdtest_pw/plugins/__init__.py,sha256=omqR6rlbxObDVFf5Dz8X4i8jmNGcY_o2Hp1hqBbKVs0,138
|
|
39
|
+
kdtest_pw/plugins/plugin_base.py,sha256=klFSvA1v4iLgonGvWFXTe3l3eWSJYxDRdbthUCZh310,4933
|
|
40
|
+
kdtest_pw/plugins/plugin_loader.py,sha256=R0pRN0qaJ8SG6-_1ZObEZKDzVRmfROwnTafqKWu7sag,7595
|
|
41
|
+
kdtest_pw/plugins/element_plus_plugin/__init__.py,sha256=JAyqZRzv1IuW4P6UvzhdmB6VMgxwtAoexuGEGsVy178,117
|
|
42
|
+
kdtest_pw/plugins/element_plus_plugin/element_plus_plugin.py,sha256=vGO44XXzfWJcgQ_Kr_x2AfLRVrhNjfRV7a2VG0iwsJ0,11100
|
|
43
|
+
kdtest_pw/plugins/element_plus_plugin/my.ini,sha256=Aow394SfyB5nZ2lBCVIj8o-w98PCK4H6jlEKtm7knjw,520
|
|
44
|
+
kdtest_pw/plugins/element_plus_plugin/elementData/elementData.yaml,sha256=nFDzGqOSwXs39oqSVO7NaTrzCBlrjYJbqlzdVwi-t5Y,2451
|
|
45
|
+
kdtest_pw/utils/__init__.py,sha256=ga9qFiHNz-WeX9qMw9F8uHdfeuZS-bq_55I9nnDSqYc,271
|
|
46
|
+
kdtest_pw/utils/built_in_function.py,sha256=3xDEGOhJl0GnYr_lYQYuIbuhFdv2czAL4aGj6ci5JOE,9405
|
|
47
|
+
kdtest_pw/utils/decorator.py,sha256=f2jEy0fYPUiAAVTFZ6O9p2UwauwEwOZPXT0FT4jC2CM,5790
|
|
48
|
+
kdtest_pw/utils/public_script.py,sha256=t-p4AwZa9L7LE9abJqE594Vcaeh6N5MVb_kGqTfTpEU,8692
|
|
49
|
+
kdtest_pw/utils/log/__init__.py,sha256=P3LllVQCC6u1-YDuuj1KGoXwxK3pdVV4ZzJPy3cmCtk,123
|
|
50
|
+
kdtest_pw/utils/log/html_report.py,sha256=gfsgv8ovO175_sOa532JjXPukIYyZblT-kmgVKhHnLI,10104
|
|
51
|
+
kdtest_pw/utils/log/logger.py,sha256=JWeMkWQrCucmz4RygA6nZ9AbHwcp6EDGsm95jEeuv_g,3264
|
|
52
|
+
kdtest_pw-2.0.0.dist-info/licenses/LICENSE,sha256=CfrnDDvu_s5bfkBBJ45r6GAnsCAXHE1XespOc4Fr0Gc,1068
|
|
53
|
+
kdtest_pw-2.0.0.dist-info/METADATA,sha256=UgyFIBFxJoFEs5VudXOfIOFnUr9zlVjXSDLfDUaVjrc,4770
|
|
54
|
+
kdtest_pw-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
55
|
+
kdtest_pw-2.0.0.dist-info/entry_points.txt,sha256=tZyewwaBHxV6S5wb9gtHeW93hKvWoUzMH-hPae_JhZ8,53
|
|
56
|
+
kdtest_pw-2.0.0.dist-info/top_level.txt,sha256=ctqaSUioNrEU1ijYI5ux0Szl3txlof2UdbyEq1Pf-Yc,10
|
|
57
|
+
kdtest_pw-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 KDTest 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 @@
|
|
|
1
|
+
kdtest_pw
|