mangoautomation 1.1.22__py3-none-any.whl → 1.1.24__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.
Potentially problematic release.
This version of mangoautomation might be problematic. Click here for more details.
- mangoautomation/enums/__init__.py +0 -1
- mangoautomation/exceptions/__init__.py +0 -2
- mangoautomation/exceptions/error_msg.py +3 -3
- mangoautomation/mangos/__init__.py +14 -0
- mangoautomation/mangos/__pycache__/__init__.cpython-310.pyc +0 -0
- mangoautomation/mangos/mangos.cp310-win_amd64.pyd +0 -0
- mangoautomation/mangos/mangos.cp312-win_amd64.pyd +0 -0
- mangoautomation/mangos/mangos.cpython-310-darwin.so +0 -0
- mangoautomation/mangos/mangos.cpython-310-x86_64-linux-gnu.so +0 -0
- mangoautomation/mangos/mangos.cpython-312-darwin.so +0 -0
- mangoautomation/mangos/mangos.cpython-312-x86_64-linux-gnu.so +0 -0
- mangoautomation/models/__init__.py +0 -1
- mangoautomation/models/_ui_model.py +1 -0
- mangoautomation/tools/__init__.py +0 -1
- mangoautomation/tools/_uiautodev.py +1 -1
- mangoautomation/uidrives/__init__.py +2 -3
- mangoautomation/uidrives/_async_element.py +74 -48
- mangoautomation/uidrives/_base_data.py +16 -4
- mangoautomation/uidrives/_driver_object.py +7 -40
- mangoautomation/uidrives/_sync_element.py +75 -46
- mangoautomation/uidrives/android/__init__.py +2 -16
- mangoautomation/uidrives/pc/__init__.py +1 -2
- mangoautomation/uidrives/pc/element.py +1 -2
- mangoautomation/uidrives/pc/input_device.py +1 -2
- mangoautomation/uidrives/web/__init__.py +5 -0
- mangoautomation/uidrives/web/{async_web/__init__.py → _async_web.py} +82 -81
- mangoautomation/uidrives/web/{sync_web/__init__.py → _sync_web.py} +80 -74
- mangoautomation-1.1.24.dist-info/METADATA +42 -0
- mangoautomation-1.1.24.dist-info/RECORD +48 -0
- {mangoautomation-1.1.22.dist-info → mangoautomation-1.1.24.dist-info}/WHEEL +1 -1
- tests/__init__.py +0 -10
- tests/demo1.py +94 -0
- tests/get_ope.py +12 -3
- tests/test_ai_element.py +36 -0
- tests/test_ui_web.py +7 -3
- mangoautomation/uidrives/android/_application.py +0 -70
- mangoautomation/uidrives/android/_assertion.py +0 -90
- mangoautomation/uidrives/android/_customization.py +0 -15
- mangoautomation/uidrives/android/_element.py +0 -169
- mangoautomation/uidrives/android/_equipment.py +0 -151
- mangoautomation/uidrives/android/_new_android.py +0 -54
- mangoautomation/uidrives/android/_page.py +0 -116
- mangoautomation/uidrives/web/async_web/_assertion.py +0 -303
- mangoautomation/uidrives/web/async_web/_browser.py +0 -112
- mangoautomation/uidrives/web/async_web/_customization.py +0 -14
- mangoautomation/uidrives/web/async_web/_element.py +0 -250
- mangoautomation/uidrives/web/async_web/_input_device.py +0 -82
- mangoautomation/uidrives/web/async_web/_new_browser.py +0 -153
- mangoautomation/uidrives/web/async_web/_page.py +0 -63
- mangoautomation/uidrives/web/sync_web/_assertion.py +0 -304
- mangoautomation/uidrives/web/sync_web/_browser.py +0 -112
- mangoautomation/uidrives/web/sync_web/_customization.py +0 -14
- mangoautomation/uidrives/web/sync_web/_element.py +0 -250
- mangoautomation/uidrives/web/sync_web/_input_device.py +0 -80
- mangoautomation/uidrives/web/sync_web/_new_browser.py +0 -153
- mangoautomation/uidrives/web/sync_web/_page.py +0 -61
- mangoautomation-1.1.22.dist-info/METADATA +0 -34
- mangoautomation-1.1.22.dist-info/RECORD +0 -59
- {mangoautomation-1.1.22.dist-info → mangoautomation-1.1.24.dist-info/licenses}/LICENSE +0 -0
- {mangoautomation-1.1.22.dist-info → mangoautomation-1.1.24.dist-info}/top_level.txt +0 -0
|
@@ -33,7 +33,7 @@ ERROR_MSG_0025 = (4025, '无法元素对应的下标,请检查通过下标获
|
|
|
33
33
|
ERROR_MSG_0026 = (4026, '')
|
|
34
34
|
ERROR_MSG_0027 = (4027,
|
|
35
35
|
'您元素的操作内容中没有任何的数据,请检查:1.页面步骤详情中字段->元素操作值是否是空,是空可能是你删除了,也可能是执行器的操作选项没有同步需要点击执行器的同步发送缓存数据;2.元素表达式错误导致查询不到元素;')
|
|
36
|
-
ERROR_MSG_0028 = (4028, '')
|
|
36
|
+
ERROR_MSG_0028 = (4028, 'AI查找元素出现顶级异常,请联系管理员检查!')
|
|
37
37
|
ERROR_MSG_0029 = (4029, '页面无元素【{}】,表达式:{}')
|
|
38
38
|
ERROR_MSG_0030 = (4030, '断言时没有找到元素')
|
|
39
39
|
ERROR_MSG_0031 = (4031, '元素未找到准备进行断言获取元素文本类容异常')
|
|
@@ -48,13 +48,13 @@ ERROR_MSG_0043 = (4043, '元素【{}】可能不存在,报错信息:{}')
|
|
|
48
48
|
ERROR_MSG_0044 = (4044, '元素【{}】可能无法消失,报错信息:{}')
|
|
49
49
|
ERROR_MSG_0045 = (4045, '设备启动超时!请检查设备是否已成功连接电脑,设备号:{}')
|
|
50
50
|
ERROR_MSG_0046 = (4046, '您要测试的设备中,没有您输入的包名,请检查包名是否正确,或者设备是否安装了这个软件包')
|
|
51
|
-
ERROR_MSG_0047 = (4047, '')
|
|
51
|
+
ERROR_MSG_0047 = (4047, 'AI定位元素超过最大次数')
|
|
52
52
|
ERROR_MSG_0048 = (4048, '选项发生错误,请在执行器点击发送缓存数据,然后重新修改步骤的操作!或联系管理员处理!')
|
|
53
53
|
ERROR_MSG_0049 = (4049, 'url格式错误,请检查url或者是选择的测试环境不正确')
|
|
54
54
|
ERROR_MSG_0050 = (4050, 'xpath定位未找到元素')
|
|
55
55
|
ERROR_MSG_0051 = (4051, '')
|
|
56
56
|
ERROR_MSG_0052 = (4052, '元素【{}】 进行断言时发生异常,请检查元素是否可以正常使用')
|
|
57
|
-
ERROR_MSG_0053 = (4053, '')
|
|
57
|
+
ERROR_MSG_0053 = (4053, '元素个数小于1,准备对元素的操作默认失败,请检查元素定位是否准确')
|
|
58
58
|
ERROR_MSG_0054 = (4054, '步骤是操作类型,但是你没有输入操作内容,请查看文档中对元素操作的介绍')
|
|
59
59
|
ERROR_MSG_0055 = (4055, '您的电脑未安装指定浏览器!')
|
|
60
60
|
ERROR_MSG_0056 = (4056, '输入的参数类型不正确,请检查操作输入框的类型是否正确')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Project: 芒果测试平台
|
|
3
|
+
# @Description:
|
|
4
|
+
# @Time : 2025-10-27 17:09
|
|
5
|
+
# @Author : 毛鹏
|
|
6
|
+
from .mangos import *
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
'ElementMain','mango_send',
|
|
10
|
+
'AsyncWebAssertion', 'AsyncWebBrowser', 'AsyncWebCustomization', 'AsyncWebElement', 'AsyncWebDeviceInput', 'AsyncWebNewBrowser', 'AsyncWebPage',
|
|
11
|
+
'SyncWebAssertion', 'SyncWebBrowser', 'SyncWebCustomization', 'SyncWebElement', 'SyncWebDeviceInput', 'SyncWebNewBrowser', 'SyncWebPage',
|
|
12
|
+
'AndroidApplication','AndroidAssertion', 'AndroidCustomization','AndroidElement','AndroidEquipment', 'NewAndroid', 'AndroidPage',
|
|
13
|
+
'WebAIFinder'
|
|
14
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,17 +3,16 @@
|
|
|
3
3
|
# @Description: # @Time : 2023-07-15 11:57
|
|
4
4
|
# @Author : 毛鹏
|
|
5
5
|
|
|
6
|
-
from .web
|
|
7
|
-
from .web.sync_web import SyncWebDevice, SyncWebCustomization
|
|
6
|
+
from .web import AsyncWebDevice, AsyncWebCustomization, SyncWebDevice, SyncWebCustomization
|
|
8
7
|
from ..uidrives._async_element import AsyncElement
|
|
9
8
|
from ..uidrives._base_data import BaseData
|
|
10
9
|
from ..uidrives._driver_object import DriverObject
|
|
11
10
|
from ..uidrives._sync_element import SyncElement
|
|
12
11
|
|
|
13
12
|
__all__ = [
|
|
14
|
-
'AsyncElement',
|
|
15
13
|
'BaseData',
|
|
16
14
|
'DriverObject',
|
|
15
|
+
'AsyncElement',
|
|
17
16
|
'SyncElement',
|
|
18
17
|
'AsyncWebDevice',
|
|
19
18
|
'SyncWebDevice',
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
# @Author : 毛鹏
|
|
6
6
|
import asyncio
|
|
7
7
|
import os
|
|
8
|
-
import random
|
|
9
8
|
import traceback
|
|
10
9
|
from typing import Optional
|
|
11
10
|
|
|
11
|
+
import itertools
|
|
12
12
|
from playwright._impl._errors import TargetClosedError, Error
|
|
13
13
|
|
|
14
|
+
from mangoautomation.mangos import WebAIFinder
|
|
15
|
+
from mangoautomation.uidrives.android import AndroidDriver
|
|
16
|
+
from mangoautomation.uidrives.web import AsyncWebDevice, AsyncWebAssertion
|
|
14
17
|
from mangotools.assertion import MangoAssertion
|
|
15
18
|
from mangotools.decorator import async_retry
|
|
16
19
|
from mangotools.enums import StatusEnum
|
|
@@ -18,8 +21,6 @@ from ..enums import ElementOperationEnum, DriveTypeEnum
|
|
|
18
21
|
from ..exceptions import MangoAutomationError
|
|
19
22
|
from ..exceptions.error_msg import *
|
|
20
23
|
from ..models import ElementResultModel, ElementModel, ElementListResultModel
|
|
21
|
-
from ..uidrives.android import AndroidDriver
|
|
22
|
-
from ..uidrives.web.async_web import AsyncWebDevice, AsyncWebAssertion
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
@@ -31,6 +32,12 @@ class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
|
31
32
|
self.element_result_model: Optional[ElementResultModel | None] = None
|
|
32
33
|
self.element_list_model: list[ElementModel] = []
|
|
33
34
|
self.test_data = self.base_data.test_data
|
|
35
|
+
self.elements = itertools.cycle([])
|
|
36
|
+
self.elements_count = 0
|
|
37
|
+
self.failed_expression = []
|
|
38
|
+
if self.base_data.is_ai:
|
|
39
|
+
self.agent = WebAIFinder(self.base_data.log, self.base_data.api_key, self.base_data.base_url,
|
|
40
|
+
self.base_data.model)
|
|
34
41
|
|
|
35
42
|
async def open_device(self, is_open: bool = False):
|
|
36
43
|
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
@@ -47,6 +54,8 @@ class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
|
47
54
|
element_model: ElementModel,
|
|
48
55
|
element_list_model: list[ElementModel] | None = None) -> ElementResultModel:
|
|
49
56
|
self.element_model = element_model
|
|
57
|
+
if self.element_model and self.element_model.elements:
|
|
58
|
+
self.elements = itertools.cycle(self.element_model.elements)
|
|
50
59
|
self.element_list_model = element_list_model
|
|
51
60
|
self.element_result_model = ElementResultModel(
|
|
52
61
|
id=self.element_model.id,
|
|
@@ -194,8 +203,6 @@ class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
|
194
203
|
await run(i.get('sql'), i.get('key_list'))
|
|
195
204
|
|
|
196
205
|
async def __custom(self):
|
|
197
|
-
for i in self.element_model.custom:
|
|
198
|
-
self.base_data.test_data.set_cache(i.get('key'), self.base_data.test_data.replace(i.get('value')))
|
|
199
206
|
for i in self.element_model.custom:
|
|
200
207
|
value = self.base_data.test_data.replace(i.get('value'))
|
|
201
208
|
self.base_data.log.debug(f'开始执行自定义-1:key: {i.get("key")}, value: {value}')
|
|
@@ -226,53 +233,72 @@ class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
|
226
233
|
ope_key = 'actual' if is_ass else 'locating'
|
|
227
234
|
for i in self.element_model.ope_value:
|
|
228
235
|
if i.f == ope_key and self.element_model.elements:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
'_type': self.element_model.type,
|
|
233
|
-
'exp': self.element_model.elements[random_element].exp,
|
|
234
|
-
'loc': self.element_model.elements[random_element].loc,
|
|
235
|
-
'sub': self.element_model.elements[random_element].sub
|
|
236
|
-
}
|
|
237
|
-
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
238
|
-
loc, ele_quantity, element_text = await self.web_find_ele(
|
|
239
|
-
**find_params, is_iframe=self.element_model.elements[random_element].is_iframe)
|
|
240
|
-
elif self.drive_type == DriveTypeEnum.ANDROID.value:
|
|
241
|
-
loc, ele_quantity, element_text = self.a_find_ele(**find_params)
|
|
242
|
-
else:
|
|
243
|
-
loc, ele_quantity, element_text = None, 0, None
|
|
244
|
-
new_element = ElementListResultModel(
|
|
245
|
-
exp=self.element_model.elements[random_element].exp,
|
|
246
|
-
loc=self.element_model.elements[random_element].loc,
|
|
247
|
-
sub=self.element_model.elements[random_element].sub,
|
|
248
|
-
ele_quantity=ele_quantity,
|
|
249
|
-
element_text=element_text,
|
|
250
|
-
is_iframe=self.element_model.elements[random_element].is_iframe
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
element_exists = any(
|
|
254
|
-
existing.exp == new_element.exp and
|
|
255
|
-
existing.loc == new_element.loc and
|
|
256
|
-
existing.sub == new_element.sub and
|
|
257
|
-
existing.is_iframe == new_element.is_iframe
|
|
258
|
-
for existing in self.element_result_model.elements
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
if not element_exists:
|
|
262
|
-
self.element_result_model.elements.append(new_element)
|
|
263
|
-
if is_ass:
|
|
264
|
-
if callable(getattr(AsyncWebAssertion, self.element_model.ope_key, None)):
|
|
265
|
-
i.v = loc
|
|
266
|
-
elif callable(getattr(MangoAssertion(), self.element_model.ope_key, None)):
|
|
267
|
-
i.v = element_text
|
|
268
|
-
else:
|
|
269
|
-
i.v = loc
|
|
270
|
-
i.v = self.base_data.test_data.replace(i.v)
|
|
236
|
+
await self.query_element(i, is_ass)
|
|
237
|
+
else:
|
|
238
|
+
i.v = self.base_data.test_data.replace(i.v)
|
|
271
239
|
except AttributeError as error:
|
|
272
240
|
self.base_data.log.debug(
|
|
273
241
|
f'获取操作值失败-1,类型:{type(error)},失败详情:{error},失败明细:{traceback.format_exc()}')
|
|
274
242
|
raise MangoAutomationError(*ERROR_MSG_0027)
|
|
275
243
|
|
|
244
|
+
async def query_element(self, i, is_ass):
|
|
245
|
+
random_element = next(self.elements)
|
|
246
|
+
self.elements_count += 1
|
|
247
|
+
find_params = {
|
|
248
|
+
'name': self.element_model.name,
|
|
249
|
+
'_type': self.element_model.type,
|
|
250
|
+
'exp': random_element.exp,
|
|
251
|
+
'loc': random_element.loc,
|
|
252
|
+
'sub': random_element.sub
|
|
253
|
+
}
|
|
254
|
+
loc_text = random_element.loc
|
|
255
|
+
try:
|
|
256
|
+
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
257
|
+
loc, ele_quantity, element_text = await self.web_find_element(**find_params,
|
|
258
|
+
is_iframe=random_element.is_iframe)
|
|
259
|
+
elif self.drive_type == DriveTypeEnum.ANDROID.value:
|
|
260
|
+
loc, ele_quantity, element_text = self.a_find_ele(**find_params)
|
|
261
|
+
else:
|
|
262
|
+
loc, ele_quantity, element_text = None, 0, None
|
|
263
|
+
except MangoAutomationError as e:
|
|
264
|
+
if not self.base_data.is_ai:
|
|
265
|
+
raise e
|
|
266
|
+
if self.elements_count < len(self.element_model.elements):
|
|
267
|
+
raise e
|
|
268
|
+
self.failed_expression.append(find_params.get('loc'))
|
|
269
|
+
self.elements_count = 0
|
|
270
|
+
loc, loc_text = await self.agent.ai_find_element_async(self.base_data.page,
|
|
271
|
+
self.element_model.name,
|
|
272
|
+
random_element.prompt,
|
|
273
|
+
failed_expression=self.failed_expression)
|
|
274
|
+
loc, ele_quantity, element_text = await self.ai_element_info(loc)
|
|
275
|
+
if loc_text not in self.failed_expression:
|
|
276
|
+
self.failed_expression.append(loc_text)
|
|
277
|
+
element_exists = any(
|
|
278
|
+
existing.exp == random_element.exp and
|
|
279
|
+
existing.loc == random_element.loc and
|
|
280
|
+
existing.sub == random_element.sub and
|
|
281
|
+
existing.is_iframe == random_element.is_iframe
|
|
282
|
+
for existing in self.element_result_model.elements
|
|
283
|
+
)
|
|
284
|
+
if not element_exists:
|
|
285
|
+
new_element = ElementListResultModel(
|
|
286
|
+
exp=random_element.exp,
|
|
287
|
+
loc=random_element.loc,
|
|
288
|
+
sub=random_element.sub,
|
|
289
|
+
ele_quantity=ele_quantity,
|
|
290
|
+
element_text=element_text,
|
|
291
|
+
is_iframe=random_element.is_iframe
|
|
292
|
+
)
|
|
293
|
+
self.element_result_model.elements.append(new_element)
|
|
294
|
+
if is_ass:
|
|
295
|
+
if callable(getattr(AsyncWebAssertion, self.element_model.ope_key, None)):
|
|
296
|
+
i.v = loc
|
|
297
|
+
elif callable(getattr(MangoAssertion(), self.element_model.ope_key, None)):
|
|
298
|
+
i.v = element_text
|
|
299
|
+
else:
|
|
300
|
+
i.v = loc
|
|
301
|
+
|
|
276
302
|
async def __error(self, msg: str, is_screenshot: bool = True):
|
|
277
303
|
try:
|
|
278
304
|
self.element_result_model.status = StatusEnum.FAIL.value
|
|
@@ -286,7 +312,7 @@ class AsyncElement(AsyncWebDevice, AndroidDriver):
|
|
|
286
312
|
"""
|
|
287
313
|
)
|
|
288
314
|
if is_screenshot:
|
|
289
|
-
file_name = f'失败截图-{self.element_model.name}{self.base_data.test_data.
|
|
315
|
+
file_name = f'失败截图-{self.element_model.name}{self.base_data.test_data.time_stamp()}.jpg'
|
|
290
316
|
await self.__error_screenshot(file_name)
|
|
291
317
|
self.element_result_model.picture_path = os.path.join(self.base_data.screenshot_path, file_name)
|
|
292
318
|
self.element_result_model.picture_name = file_name
|
|
@@ -7,8 +7,8 @@ from typing import Optional
|
|
|
7
7
|
from unittest.mock import MagicMock
|
|
8
8
|
|
|
9
9
|
import sys
|
|
10
|
-
from playwright.async_api import Page as
|
|
11
|
-
from playwright.sync_api import Page as
|
|
10
|
+
from playwright.async_api import Page as AsyncPage, BrowserContext as AsyncBrowserContext
|
|
11
|
+
from playwright.sync_api import Page as SyncPage, BrowserContext as SyncBrowserContext
|
|
12
12
|
from uiautomator2 import Device
|
|
13
13
|
|
|
14
14
|
from mangoautomation.enums import DriveTypeEnum
|
|
@@ -35,13 +35,18 @@ class BaseData:
|
|
|
35
35
|
self.download_path: Optional[str | None] = None
|
|
36
36
|
self.screenshot_path: Optional[str | None] = None
|
|
37
37
|
|
|
38
|
+
self.is_ai = False
|
|
39
|
+
self.api_key = None
|
|
40
|
+
self.base_url = None
|
|
41
|
+
self.model = None
|
|
42
|
+
|
|
38
43
|
self.mysql_config: Optional[MysqlConingModel | None] = None
|
|
39
44
|
self.mysql_connect: Optional[MysqlConnect | None] = None
|
|
40
45
|
|
|
41
46
|
self.url: Optional[str | None] = None
|
|
42
47
|
self.is_open_url = False
|
|
43
|
-
self.page: Optional[
|
|
44
|
-
self.context: Optional[
|
|
48
|
+
self.page: Optional[AsyncPage | SyncPage | None] = None
|
|
49
|
+
self.context: Optional[AsyncBrowserContext | SyncBrowserContext | None] = None
|
|
45
50
|
|
|
46
51
|
self.package_name: Optional[str | None] = None
|
|
47
52
|
self.android: Optional[Device | None] = None
|
|
@@ -116,3 +121,10 @@ class BaseData:
|
|
|
116
121
|
raise MangoAutomationError(*ERROR_MSG_0007)
|
|
117
122
|
else:
|
|
118
123
|
pass
|
|
124
|
+
|
|
125
|
+
def set_agent(self, is_ai: bool, api_key: str, base_url: str='https://api.siliconflow.cn/v1', model: str='THUDM/GLM-Z1-9B-0414'):
|
|
126
|
+
self.is_ai = is_ai
|
|
127
|
+
self.api_key = api_key
|
|
128
|
+
self.base_url = base_url
|
|
129
|
+
self.model = model
|
|
130
|
+
return self
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
# @Author : 毛鹏
|
|
6
6
|
|
|
7
7
|
from typing import Optional
|
|
8
|
-
|
|
9
|
-
from ..uidrives.android._new_android import NewAndroid
|
|
8
|
+
from mangoautomation.mangos import NewAndroid, AsyncWebNewBrowser, SyncWebNewBrowser
|
|
10
9
|
from ..uidrives.pc.new_windows import NewWindows
|
|
11
|
-
|
|
12
|
-
from ..uidrives.web.sync_web._new_browser import SyncWebNewBrowser
|
|
10
|
+
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class DriverObject:
|
|
@@ -21,46 +19,15 @@ class DriverObject:
|
|
|
21
19
|
self.android: Optional[NewAndroid] = None
|
|
22
20
|
self.windows: Optional[NewWindows] = None
|
|
23
21
|
|
|
24
|
-
def set_web(self,
|
|
25
|
-
|
|
26
|
-
web_path: str | None = None,
|
|
27
|
-
web_max=False,
|
|
28
|
-
web_headers=False,
|
|
29
|
-
web_recording=False,
|
|
30
|
-
web_h5=None,
|
|
31
|
-
is_header_intercept=False,
|
|
32
|
-
web_is_default=False,
|
|
33
|
-
videos_path=None
|
|
34
|
-
):
|
|
22
|
+
def set_web(self, **kwargs):
|
|
23
|
+
kwargs['log'] = self.log
|
|
35
24
|
if self.is_async:
|
|
36
|
-
self.web = AsyncWebNewBrowser(
|
|
37
|
-
web_type,
|
|
38
|
-
web_path,
|
|
39
|
-
web_max,
|
|
40
|
-
web_headers,
|
|
41
|
-
web_recording,
|
|
42
|
-
web_h5,
|
|
43
|
-
is_header_intercept,
|
|
44
|
-
web_is_default,
|
|
45
|
-
videos_path,
|
|
46
|
-
log=self.log,
|
|
47
|
-
)
|
|
25
|
+
self.web = AsyncWebNewBrowser(**kwargs)
|
|
48
26
|
else:
|
|
49
|
-
self.web = SyncWebNewBrowser(
|
|
50
|
-
web_type,
|
|
51
|
-
web_path,
|
|
52
|
-
web_max,
|
|
53
|
-
web_headers,
|
|
54
|
-
web_recording,
|
|
55
|
-
web_h5,
|
|
56
|
-
is_header_intercept,
|
|
57
|
-
web_is_default,
|
|
58
|
-
videos_path,
|
|
59
|
-
log=self.log,
|
|
60
|
-
)
|
|
27
|
+
self.web = SyncWebNewBrowser(**kwargs)
|
|
61
28
|
|
|
62
29
|
def set_android(self, and_equipment: str):
|
|
63
30
|
self.android = NewAndroid(and_equipment)
|
|
64
31
|
|
|
65
32
|
def set_windows(self, win_path: str, win_title: str):
|
|
66
|
-
self.windows = NewWindows(win_path, win_title)
|
|
33
|
+
self.windows = NewWindows(win_path, win_title)
|
|
@@ -4,22 +4,23 @@
|
|
|
4
4
|
# @Time : 2025-04-12 15:55
|
|
5
5
|
# @Author : 毛鹏
|
|
6
6
|
import os
|
|
7
|
-
import random
|
|
8
7
|
import traceback
|
|
9
8
|
from typing import Optional
|
|
10
9
|
|
|
10
|
+
import itertools
|
|
11
11
|
import time
|
|
12
12
|
from playwright._impl._errors import TargetClosedError, Error
|
|
13
13
|
|
|
14
14
|
from mangotools.assertion import MangoAssertion
|
|
15
15
|
from mangotools.decorator import sync_retry
|
|
16
16
|
from mangotools.enums import StatusEnum
|
|
17
|
+
from mangoautomation.mangos import WebAIFinder
|
|
17
18
|
from ..enums import ElementOperationEnum, DriveTypeEnum
|
|
18
19
|
from ..exceptions import MangoAutomationError
|
|
19
20
|
from ..exceptions.error_msg import *
|
|
20
21
|
from ..models import ElementResultModel, ElementModel, ElementListResultModel
|
|
21
22
|
from ..uidrives.android import AndroidDriver
|
|
22
|
-
from ..uidrives.web
|
|
23
|
+
from ..uidrives.web import SyncWebDevice, SyncWebAssertion
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class SyncElement(SyncWebDevice, AndroidDriver):
|
|
@@ -31,6 +32,12 @@ class SyncElement(SyncWebDevice, AndroidDriver):
|
|
|
31
32
|
self.element_result_model: Optional[ElementResultModel | None] = None
|
|
32
33
|
self.element_list_model: Optional[list[ElementModel] | None] = None
|
|
33
34
|
self.test_data = self.base_data.test_data
|
|
35
|
+
self.elements = itertools.cycle([])
|
|
36
|
+
self.elements_count = 0
|
|
37
|
+
self.failed_expression = []
|
|
38
|
+
if self.base_data.is_ai:
|
|
39
|
+
self.agent = WebAIFinder(self.base_data.log, self.base_data.api_key, self.base_data.base_url,
|
|
40
|
+
self.base_data.model)
|
|
34
41
|
|
|
35
42
|
def open_device(self, is_open: bool = False):
|
|
36
43
|
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
@@ -47,6 +54,8 @@ class SyncElement(SyncWebDevice, AndroidDriver):
|
|
|
47
54
|
element_model: ElementModel,
|
|
48
55
|
element_list_model: list[ElementModel] | None = None) -> ElementResultModel:
|
|
49
56
|
self.element_model = element_model
|
|
57
|
+
if self.element_model and self.element_model.elements:
|
|
58
|
+
self.elements = itertools.cycle(self.element_model.elements)
|
|
50
59
|
self.element_list_model = element_list_model
|
|
51
60
|
self.element_result_model = ElementResultModel(
|
|
52
61
|
id=self.element_model.id,
|
|
@@ -195,7 +204,9 @@ class SyncElement(SyncWebDevice, AndroidDriver):
|
|
|
195
204
|
|
|
196
205
|
def __custom(self):
|
|
197
206
|
for i in self.element_model.custom:
|
|
198
|
-
|
|
207
|
+
value = self.base_data.test_data.replace(i.get('value'))
|
|
208
|
+
self.base_data.log.debug(f'开始执行自定义-1:key: {i.get("key")}, value: {value}')
|
|
209
|
+
self.base_data.test_data.set_cache(i.get('key'), value)
|
|
199
210
|
|
|
200
211
|
def __condition(self):
|
|
201
212
|
error_list = []
|
|
@@ -222,54 +233,72 @@ class SyncElement(SyncWebDevice, AndroidDriver):
|
|
|
222
233
|
ope_key = 'actual' if is_ass else 'locating'
|
|
223
234
|
for i in self.element_model.ope_value:
|
|
224
235
|
if i.f == ope_key and self.element_model.elements:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
'name': self.element_model.name,
|
|
228
|
-
'_type': self.element_model.type,
|
|
229
|
-
'exp': self.element_model.elements[random_element].exp,
|
|
230
|
-
'loc': self.element_model.elements[random_element].loc,
|
|
231
|
-
'sub': self.element_model.elements[random_element].sub
|
|
232
|
-
}
|
|
233
|
-
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
234
|
-
loc, ele_quantity, element_text = self.web_find_ele(
|
|
235
|
-
**find_params, is_iframe=self.element_model.elements[random_element].is_iframe)
|
|
236
|
-
elif self.drive_type == DriveTypeEnum.ANDROID.value:
|
|
237
|
-
loc, ele_quantity, element_text = self.a_find_ele(**find_params)
|
|
238
|
-
else:
|
|
239
|
-
loc, ele_quantity, element_text = None, 0, None
|
|
240
|
-
new_element = ElementListResultModel(
|
|
241
|
-
exp=self.element_model.elements[random_element].exp,
|
|
242
|
-
loc=self.element_model.elements[random_element].loc,
|
|
243
|
-
sub=self.element_model.elements[random_element].sub,
|
|
244
|
-
ele_quantity=ele_quantity,
|
|
245
|
-
element_text=element_text,
|
|
246
|
-
is_iframe=self.element_model.elements[random_element].is_iframe
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
element_exists = any(
|
|
250
|
-
existing.exp == new_element.exp and
|
|
251
|
-
existing.loc == new_element.loc and
|
|
252
|
-
existing.sub == new_element.sub and
|
|
253
|
-
existing.is_iframe == new_element.is_iframe
|
|
254
|
-
for existing in self.element_result_model.elements
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
if not element_exists:
|
|
258
|
-
self.element_result_model.elements.append(new_element)
|
|
259
|
-
if is_ass:
|
|
260
|
-
if callable(getattr(SyncWebAssertion, self.element_model.ope_key, None)):
|
|
261
|
-
i.v = loc
|
|
262
|
-
elif callable(getattr(MangoAssertion(), self.element_model.ope_key, None)):
|
|
263
|
-
i.v = element_text
|
|
264
|
-
else:
|
|
265
|
-
i.v = loc
|
|
236
|
+
self.query_element(i, is_ass)
|
|
237
|
+
else:
|
|
266
238
|
i.v = self.base_data.test_data.replace(i.v)
|
|
267
|
-
|
|
268
239
|
except AttributeError as error:
|
|
269
240
|
self.base_data.log.debug(
|
|
270
241
|
f'获取操作值失败-1,类型:{type(error)},失败详情:{error},失败明细:{traceback.format_exc()}')
|
|
271
242
|
raise MangoAutomationError(*ERROR_MSG_0027)
|
|
272
243
|
|
|
244
|
+
def query_element(self, i, is_ass):
|
|
245
|
+
random_element = next(self.elements)
|
|
246
|
+
self.elements_count += 1
|
|
247
|
+
find_params = {
|
|
248
|
+
'name': self.element_model.name,
|
|
249
|
+
'_type': self.element_model.type,
|
|
250
|
+
'exp': random_element.exp,
|
|
251
|
+
'loc': random_element.loc,
|
|
252
|
+
'sub': random_element.sub
|
|
253
|
+
}
|
|
254
|
+
loc_text = random_element.loc
|
|
255
|
+
try:
|
|
256
|
+
if self.drive_type == DriveTypeEnum.WEB.value:
|
|
257
|
+
loc, ele_quantity, element_text = self.web_find_element(**find_params,
|
|
258
|
+
is_iframe=random_element.is_iframe)
|
|
259
|
+
elif self.drive_type == DriveTypeEnum.ANDROID.value:
|
|
260
|
+
loc, ele_quantity, element_text = self.a_find_ele(**find_params)
|
|
261
|
+
else:
|
|
262
|
+
loc, ele_quantity, element_text = None, 0, None
|
|
263
|
+
except MangoAutomationError as e:
|
|
264
|
+
if not self.base_data.is_ai:
|
|
265
|
+
raise e
|
|
266
|
+
if self.elements_count < len(self.element_model.elements):
|
|
267
|
+
raise e
|
|
268
|
+
self.failed_expression.append(find_params.get('loc'))
|
|
269
|
+
self.elements_count = 0
|
|
270
|
+
loc, loc_text = self.agent.ai_find_element_sync(self.base_data.page,
|
|
271
|
+
self.element_model.name,
|
|
272
|
+
random_element.prompt,
|
|
273
|
+
failed_expression=self.failed_expression)
|
|
274
|
+
loc, ele_quantity, element_text = self.ai_element_info(loc)
|
|
275
|
+
if loc_text not in self.failed_expression:
|
|
276
|
+
self.failed_expression.append(loc_text)
|
|
277
|
+
element_exists = any(
|
|
278
|
+
existing.exp == random_element.exp and
|
|
279
|
+
existing.loc == random_element.loc and
|
|
280
|
+
existing.sub == random_element.sub and
|
|
281
|
+
existing.is_iframe == random_element.is_iframe
|
|
282
|
+
for existing in self.element_result_model.elements
|
|
283
|
+
)
|
|
284
|
+
if not element_exists:
|
|
285
|
+
new_element = ElementListResultModel(
|
|
286
|
+
exp=random_element.exp,
|
|
287
|
+
loc=random_element.loc,
|
|
288
|
+
sub=random_element.sub,
|
|
289
|
+
ele_quantity=ele_quantity,
|
|
290
|
+
element_text=element_text,
|
|
291
|
+
is_iframe=random_element.is_iframe
|
|
292
|
+
)
|
|
293
|
+
self.element_result_model.elements.append(new_element)
|
|
294
|
+
if is_ass:
|
|
295
|
+
if callable(getattr(SyncWebAssertion, self.element_model.ope_key, None)):
|
|
296
|
+
i.v = loc
|
|
297
|
+
elif callable(getattr(MangoAssertion(), self.element_model.ope_key, None)):
|
|
298
|
+
i.v = element_text
|
|
299
|
+
else:
|
|
300
|
+
i.v = loc
|
|
301
|
+
|
|
273
302
|
def __error(self, msg: str, is_screenshot: bool = True):
|
|
274
303
|
try:
|
|
275
304
|
self.element_result_model.status = StatusEnum.FAIL.value
|
|
@@ -283,7 +312,7 @@ class SyncElement(SyncWebDevice, AndroidDriver):
|
|
|
283
312
|
"""
|
|
284
313
|
)
|
|
285
314
|
if is_screenshot:
|
|
286
|
-
file_name = f'失败截图-{self.element_model.name}{self.base_data.test_data.
|
|
315
|
+
file_name = f'失败截图-{self.element_model.name}{self.base_data.test_data.time_stamp()}.jpg'
|
|
287
316
|
self.__error_screenshot(file_name)
|
|
288
317
|
self.element_result_model.picture_path = os.path.join(self.base_data.screenshot_path, file_name)
|
|
289
318
|
self.element_result_model.picture_name = file_name
|
|
@@ -8,26 +8,13 @@ from uiautomator2 import UiObject, UiObjectNotFoundError
|
|
|
8
8
|
from uiautomator2.exceptions import XPathElementNotFoundError
|
|
9
9
|
from uiautomator2.xpath import XPathSelector
|
|
10
10
|
|
|
11
|
+
from mangoautomation.mangos import AndroidApplication, AndroidAssertion, AndroidElement, AndroidCustomization, \
|
|
12
|
+
AndroidEquipment, AndroidPage
|
|
11
13
|
from mangotools.assertion import MangoAssertion
|
|
12
|
-
from ..android._application import AndroidApplication
|
|
13
|
-
from ..android._assertion import AndroidAssertion
|
|
14
|
-
from ..android._customization import AndroidCustomization
|
|
15
|
-
from ..android._element import AndroidElement
|
|
16
|
-
from ..android._equipment import AndroidEquipment
|
|
17
|
-
from ..android._page import AndroidPage
|
|
18
14
|
from ...enums import ElementExpEnum
|
|
19
15
|
from ...exceptions import MangoAutomationError
|
|
20
16
|
from ...exceptions.error_msg import *
|
|
21
17
|
|
|
22
|
-
__all__ = [
|
|
23
|
-
'AndroidApplication',
|
|
24
|
-
'AndroidAssertion',
|
|
25
|
-
'AndroidElement',
|
|
26
|
-
'AndroidCustomization',
|
|
27
|
-
'AndroidEquipment',
|
|
28
|
-
'AndroidPage',
|
|
29
|
-
'AndroidDriver',
|
|
30
|
-
]
|
|
31
18
|
|
|
32
19
|
from mangotools.mangos import Mango
|
|
33
20
|
|
|
@@ -68,7 +55,6 @@ class AndroidDriver(AndroidPage,
|
|
|
68
55
|
is_method = callable(getattr(AndroidAssertion(self.base_data), ope_key, None))
|
|
69
56
|
if is_method and ope_value.get('actual') is None:
|
|
70
57
|
raise MangoAutomationError(*ERROR_MSG_0031, value=(name,))
|
|
71
|
-
|
|
72
58
|
try:
|
|
73
59
|
if is_method:
|
|
74
60
|
self.base_data.log.debug(f'开始断言-1,方法:{ope_key},断言值:{ope_value}')
|
|
@@ -24,13 +24,12 @@ else:
|
|
|
24
24
|
from uiautomation import Control
|
|
25
25
|
import uiautomation
|
|
26
26
|
|
|
27
|
-
from mangoautomation.uidrives._base_data import BaseData
|
|
28
27
|
from mangoautomation.uidrives.pc.element import WinElement
|
|
29
28
|
from mangoautomation.uidrives.pc.input_device import WinDeviceInput
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class WinDriver(WinElement, WinDeviceInput):
|
|
33
|
-
def __init__(self, base_data
|
|
32
|
+
def __init__(self, base_data):
|
|
34
33
|
super().__init__(base_data)
|
|
35
34
|
|
|
36
35
|
def find_element(
|