mangoautomation 1.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.

Potentially problematic release.


This version of mangoautomation might be problematic. Click here for more details.

Files changed (55) hide show
  1. mangoautomation/__init__.py +5 -0
  2. mangoautomation/enums/__init__.py +19 -0
  3. mangoautomation/enums/_base_enum.py +38 -0
  4. mangoautomation/enums/_ui_enum.py +110 -0
  5. mangoautomation/exceptions/__init__.py +14 -0
  6. mangoautomation/exceptions/_error_msg.py +73 -0
  7. mangoautomation/exceptions/_exceptions.py +14 -0
  8. mangoautomation/models/__init__.py +15 -0
  9. mangoautomation/models/_ui_model.py +61 -0
  10. mangoautomation/tools/__init__.py +13 -0
  11. mangoautomation/tools/_mate.py +12 -0
  12. mangoautomation/uidrive/__init__.py +20 -0
  13. mangoautomation/uidrive/_async_element.py +286 -0
  14. mangoautomation/uidrive/_base_data.py +103 -0
  15. mangoautomation/uidrive/_driver_object.py +63 -0
  16. mangoautomation/uidrive/_sync_element.py +286 -0
  17. mangoautomation/uidrive/android/__init__.py +127 -0
  18. mangoautomation/uidrive/android/_application.py +69 -0
  19. mangoautomation/uidrive/android/_assertion.py +84 -0
  20. mangoautomation/uidrive/android/_customization.py +15 -0
  21. mangoautomation/uidrive/android/_element.py +168 -0
  22. mangoautomation/uidrive/android/_equipment.py +150 -0
  23. mangoautomation/uidrive/android/_new_android.py +54 -0
  24. mangoautomation/uidrive/android/_page.py +116 -0
  25. mangoautomation/uidrive/pc/__init__.py +80 -0
  26. mangoautomation/uidrive/pc/assertion.py +5 -0
  27. mangoautomation/uidrive/pc/customization.py +10 -0
  28. mangoautomation/uidrive/pc/element.py +21 -0
  29. mangoautomation/uidrive/pc/input_device.py +14 -0
  30. mangoautomation/uidrive/pc/new_windows.py +79 -0
  31. mangoautomation/uidrive/web/__init__.py +5 -0
  32. mangoautomation/uidrive/web/async_web/__init__.py +174 -0
  33. mangoautomation/uidrive/web/async_web/_assertion.py +290 -0
  34. mangoautomation/uidrive/web/async_web/_browser.py +97 -0
  35. mangoautomation/uidrive/web/async_web/_customization.py +14 -0
  36. mangoautomation/uidrive/web/async_web/_element.py +199 -0
  37. mangoautomation/uidrive/web/async_web/_input_device.py +83 -0
  38. mangoautomation/uidrive/web/async_web/_new_browser.py +151 -0
  39. mangoautomation/uidrive/web/async_web/_page.py +62 -0
  40. mangoautomation/uidrive/web/sync_web/__init__.py +174 -0
  41. mangoautomation/uidrive/web/sync_web/_assertion.py +282 -0
  42. mangoautomation/uidrive/web/sync_web/_browser.py +96 -0
  43. mangoautomation/uidrive/web/sync_web/_customization.py +14 -0
  44. mangoautomation/uidrive/web/sync_web/_element.py +198 -0
  45. mangoautomation/uidrive/web/sync_web/_input_device.py +79 -0
  46. mangoautomation/uidrive/web/sync_web/_new_browser.py +146 -0
  47. mangoautomation/uidrive/web/sync_web/_page.py +61 -0
  48. mangoautomation-1.0.0.dist-info/LICENSE +21 -0
  49. mangoautomation-1.0.0.dist-info/METADATA +30 -0
  50. mangoautomation-1.0.0.dist-info/RECORD +55 -0
  51. mangoautomation-1.0.0.dist-info/WHEEL +5 -0
  52. mangoautomation-1.0.0.dist-info/top_level.txt +2 -0
  53. tests/__init__.py +5 -0
  54. tests/test_ui_and.py +29 -0
  55. tests/test_ui_web.py +77 -0
@@ -0,0 +1,79 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description:
4
+ # @Time : 2024-11-30 13:12
5
+ # @Author : 毛鹏
6
+ import subprocess
7
+ from typing import Optional
8
+
9
+ import sys
10
+ import time
11
+
12
+ if not sys.platform.startswith('win32'):
13
+ print("警告: uiautomation 仅支持 Windows,当前环境已自动跳过")
14
+
15
+
16
+ class Auto:
17
+ pass
18
+
19
+
20
+ auto = Auto()
21
+
22
+
23
+ class Control:
24
+ pass
25
+ else:
26
+
27
+ from uiautomation import Control, WindowControl
28
+ import uiautomation
29
+
30
+
31
+ class NewWindows:
32
+
33
+ def __init__(self, win_path: str, win_title: str, timeout=10):
34
+ self.win_path = win_path
35
+ self.win_title = win_title
36
+ self.timeout = timeout
37
+ self.window: Optional[None | WindowControl] = None
38
+
39
+ def new_windows(self, retry=3):
40
+ subprocess.Popen(self.win_path)
41
+ for _ in range(retry):
42
+ self.window = uiautomation.WindowControl(
43
+ searchDepth=1,
44
+ Name=self.win_title,
45
+ Timeout=self.timeout * 1000
46
+ )
47
+ if self.window.Exists():
48
+ return self.window
49
+ time.sleep(1)
50
+
51
+ def find_control(self,
52
+ parent=None,
53
+ control_type: str = "Control",
54
+ name: str = None,
55
+ automation_id: str = None,
56
+ **kwargs) -> Control:
57
+ """
58
+ 查找控件(支持多条件组合)
59
+ :param parent: 父控件(默认从窗口开始查找)
60
+ :param control_type: 控件类型(如 Button、Edit、ListItem)
61
+ :param name: 控件名称(Name 属性)
62
+ :param automation_id: AutomationId 属性
63
+ :return: Control 对象
64
+ """
65
+ search_params = {
66
+ "searchDepth": 2,
67
+ "Timeout": self.timeout * 1000,
68
+ **kwargs
69
+ }
70
+ if name:
71
+ search_params["Name"] = name
72
+ if automation_id:
73
+ search_params["AutomationId"] = automation_id
74
+
75
+ parent = parent or self.window
76
+ control = getattr(uiautomation, f"{control_type}Control")(parent=parent, **search_params)
77
+ if not control.Exists():
78
+ raise Exception(f"未找到控件: {control_type}/{name}/{automation_id}")
79
+ return control
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description:
4
+ # @Time : 2023/5/16 14:50
5
+ # @Author : 毛鹏
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description:
4
+ # @Time : 2025-04-04 21:42
5
+ # @Author : 毛鹏
6
+ import re
7
+ import traceback
8
+
9
+ from playwright._impl._errors import TimeoutError, Error, TargetClosedError
10
+ from playwright.async_api._generated import Locator
11
+
12
+ from mangotools.assertion import PublicAssertion, SqlAssertion
13
+ from mangotools.mangos import Mango
14
+ from mangotools.enums import StatusEnum
15
+ from ....enums import ElementExpEnum
16
+ from ....exceptions import MangoAutomationError
17
+ from ....exceptions._error_msg import *
18
+ from ....uidrive._base_data import BaseData
19
+ from ....uidrive.web.async_web._assertion import AsyncWebAssertion
20
+ from ....uidrive.web.async_web._browser import AsyncWebBrowser
21
+ from ....uidrive.web.async_web._customization import AsyncWebCustomization
22
+ from ....uidrive.web.async_web._element import AsyncWebElement
23
+ from ....uidrive.web.async_web._input_device import AsyncWebDeviceInput
24
+ from ....uidrive.web.async_web._page import AsyncWebPage
25
+
26
+ re = re
27
+ __all__ = [
28
+ 'AsyncWebPage',
29
+ 'AsyncWebElement',
30
+ 'AsyncWebDeviceInput',
31
+ 'AsyncWebCustomization',
32
+ 'AsyncWebBrowser',
33
+ 'AsyncWebAssertion',
34
+ 'AsyncWebDevice',
35
+ ]
36
+
37
+
38
+ class AsyncWebDevice(AsyncWebBrowser,
39
+ AsyncWebPage,
40
+ AsyncWebElement,
41
+ AsyncWebDeviceInput,
42
+ AsyncWebCustomization):
43
+
44
+ def __init__(self, base_data: BaseData):
45
+ super().__init__(base_data)
46
+
47
+ async def open_url(self, is_open: bool = False):
48
+ if not self.base_data.is_open_url or is_open:
49
+ self.base_data.log.debug(f'打开url,is_open_url:{self.base_data.is_open_url},url:{self.base_data.url}')
50
+ await self.w_goto(self.base_data.url)
51
+ self.base_data.is_open_url = True
52
+
53
+ async def web_action_element(self, name, ope_key, ope_value, ):
54
+ self.base_data.log.debug(f'操作元素,名称:{name},key:{ope_key},value:{ope_value}')
55
+ try:
56
+ await Mango.a_e(self, ope_key, ope_value)
57
+ except TimeoutError as error:
58
+ self.base_data.log.error(f'WEB自动化操作失败-1,类型:{type(error)},失败详情:{error}')
59
+ raise MangoAutomationError(*ERROR_MSG_0011, value=(name,))
60
+ except TargetClosedError:
61
+ self.base_data.setup()
62
+ raise MangoAutomationError(*ERROR_MSG_0010)
63
+ except Error as error:
64
+ self.base_data.log.error(f'WEB自动化操作失败-2,类型:{type(error)},失败详情:{error}')
65
+ raise MangoAutomationError(*ERROR_MSG_0032, value=(name,))
66
+ except ValueError as error:
67
+ self.base_data.log.error(f'WEB自动化操作失败-3,类型:{type(error)},失败详情:{error}')
68
+ raise MangoAutomationError(*ERROR_MSG_0012)
69
+
70
+ async def web_assertion_element(self, name, ope_key, ope_value):
71
+ self.base_data.log.debug(f'断言元素,名称:{name},key:{ope_key},value:{ope_value}')
72
+ is_method = callable(getattr(AsyncWebAssertion, ope_key, None))
73
+ is_method_public = callable(getattr(PublicAssertion, ope_key, None))
74
+ try:
75
+ if is_method:
76
+ if ope_value.get('actual', None) is None:
77
+ traceback.print_exc()
78
+ raise MangoAutomationError(*ERROR_MSG_0031, value=(name,))
79
+ self.base_data.log.debug(f'开始断言-1,方法:{ope_key},断言值:{ope_value}')
80
+ await Mango.a_e(AsyncWebAssertion(self.base_data), ope_key, ope_value)
81
+ elif is_method_public:
82
+ self.base_data.log.debug(f'开始断言-2,方法:{ope_key},断言值:{ope_value}')
83
+ Mango.s_e(PublicAssertion, ope_key, ope_value)
84
+ else:
85
+ if self.base_data.mysql_connect is not None:
86
+ SqlAssertion.mysql_obj = self.base_data.mysql_connect
87
+ self.base_data.log.debug(f'开始断言-3,方法:sql相等端游,实际值:{ope_value}')
88
+ await SqlAssertion.sql_is_equal(**ope_value)
89
+ else:
90
+ raise MangoAutomationError(*ERROR_MSG_0019)
91
+ except AssertionError as error:
92
+ self.base_data.log.debug(f'WEB自动化断言失败-1,类型:{type(error)},失败详情:{error}')
93
+ raise MangoAutomationError(*ERROR_MSG_0017, value=error.args)
94
+ except AttributeError as error:
95
+ self.base_data.log.error(f'WEB自动化断言失败-2,类型:{type(error)},失败详情:{error}')
96
+ raise MangoAutomationError(*ERROR_MSG_0048)
97
+ except ValueError as error:
98
+ self.base_data.log.error(f'WEB自动化断言失败-3,类型:{type(error)},失败详情:{error}')
99
+ raise MangoAutomationError(*ERROR_MSG_0005)
100
+ except TargetClosedError:
101
+ self.base_data.setup()
102
+ raise MangoAutomationError(*ERROR_MSG_0010)
103
+ except Error as error:
104
+ self.base_data.log.error(f'WEB自动化断言失败-4,类型:{type(error)},失败详情:{error}')
105
+ raise MangoAutomationError(*ERROR_MSG_0052, value=(name,), )
106
+
107
+ async def web_find_ele(self, name, _type, exp, loc, sub, is_iframe) \
108
+ -> tuple[Locator, int, str] | tuple[list[Locator], int, str]:
109
+ self.base_data.log.debug(
110
+ f'查找元素,名称:{name},_type:{_type},exp:{exp},loc:{loc},sub:{sub},is_iframe:{is_iframe}')
111
+ if is_iframe != StatusEnum.SUCCESS.value:
112
+ locator: Locator = await self.__find_ele(self.base_data.page, exp, loc)
113
+ try:
114
+ count = await locator.count()
115
+ loc = locator.nth(sub - 1) if sub else locator
116
+ try:
117
+ text = await self.w_get_text(loc)
118
+ except Exception:
119
+ text = None
120
+ return loc, count, text
121
+ except Error as error:
122
+ self.base_data.log.error(
123
+ f'WEB自动化查找元素失败-1,类型:{type(error)},失败详情:{error},失败明细:{traceback.format_exc()}')
124
+ raise MangoAutomationError(*ERROR_MSG_0041, value=(name, loc))
125
+ else:
126
+ ele_list: list[Locator] = []
127
+ for i in self.base_data.page.frames:
128
+ locator: Locator = await self.__find_ele(i, exp, loc)
129
+ try:
130
+ count = await locator.count()
131
+ except Error as error:
132
+ self.base_data.log.error(
133
+ f'WEB自动化查找元素失败-2,类型:{type(error)},失败详情:{error},失败明细:{traceback.format_exc()}')
134
+ raise MangoAutomationError(*ERROR_MSG_0041, )
135
+ if count > 0:
136
+ for nth in range(0, count):
137
+ ele_list.append(locator.nth(nth))
138
+ else:
139
+ raise MangoAutomationError(*ERROR_MSG_0023)
140
+ try:
141
+ count = len(ele_list)
142
+ loc = ele_list[sub - 1] if sub else ele_list
143
+ try:
144
+ text = await self.w_get_text(loc)
145
+ except Exception:
146
+ text = None
147
+ return loc, count, text
148
+ except IndexError:
149
+ raise MangoAutomationError(*ERROR_MSG_0025, value=(len(ele_list),))
150
+
151
+ async def __find_ele(self, page, exp, loc) -> Locator:
152
+ match exp:
153
+ case ElementExpEnum.LOCATOR.value:
154
+ try:
155
+ return eval(f"await page.{loc}")
156
+ except SyntaxError:
157
+ try:
158
+ return eval(f"page.{loc}")
159
+ except SyntaxError as error:
160
+ self.base_data.log.error(f'WEB自动化查找元素失败-3,类型:{type(error)},失败详情:{error}')
161
+ raise MangoAutomationError(*ERROR_MSG_0022)
162
+ except NameError as error:
163
+ self.base_data.log.error(f'WEB自动化查找元素失败-4,类型:{type(error)},失败详情:{error}')
164
+ raise MangoAutomationError(*ERROR_MSG_0060)
165
+ case ElementExpEnum.XPATH.value:
166
+ return page.locator(f'xpath={loc}')
167
+ case ElementExpEnum.CSS.value:
168
+ return page.locator(loc)
169
+ case ElementExpEnum.TEXT.value:
170
+ return page.get_by_text(loc, exact=True)
171
+ case ElementExpEnum.PLACEHOLDER.value:
172
+ return page.get_by_placeholder(loc)
173
+ case _:
174
+ raise MangoAutomationError(*ERROR_MSG_0020)
@@ -0,0 +1,290 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description: # @Time : 2023-04-26 22:25
4
+ # @Author : 毛鹏
5
+
6
+ from playwright.async_api import Locator, expect as exp
7
+
8
+ from mangotools.decorator import async_method_callback
9
+ from mangotools.models import MethodModel
10
+ from ....exceptions import MangoAutomationError
11
+ from ....exceptions._error_msg import ERROR_MSG_0021
12
+ from ....tools import Meta
13
+ from ....uidrive._base_data import BaseData
14
+
15
+
16
+ class AsyncWebAssertion(metaclass=Meta):
17
+ """元素断言"""
18
+
19
+ def __init__(self, base_data: BaseData):
20
+ self.base_data = base_data
21
+
22
+ @async_method_callback('ass_web', '元素断言', 0, [
23
+ MethodModel(f='actual'),
24
+ MethodModel(f='expect', p='请输入元素个数', d=True)])
25
+ async def w_to_have_count(self, actual: Locator, expect: str):
26
+ """元素是几个"""
27
+ try:
28
+ await exp(actual).to_have_count(int(expect))
29
+ except AssertionError as e:
30
+ raise AssertionError(f'实际={await actual.count()}, 预期={expect}') from e
31
+
32
+ @async_method_callback('ass_web', '元素断言', 1, [MethodModel(f='actual')])
33
+ async def w_all_not_to_be_empty(self, actual: Locator):
34
+ """元素存在"""
35
+ count = await actual.count()
36
+ if count == 0:
37
+ assert False, f'实际={count}, 预期>0'
38
+
39
+ @async_method_callback('ass_web', '元素断言', 1, [
40
+ MethodModel(f='actual'),
41
+ MethodModel(f='expect', p='请输入元素存输入1,不存输入0', d=True)])
42
+ async def w_to_element_count(self, actual: Locator, expect: int):
43
+ """元素是否存在"""
44
+ if int(expect) == 0:
45
+ assert actual is None, f'实际={actual}, 预期={expect}'
46
+ else:
47
+ if actual:
48
+ try:
49
+ await exp(actual).to_have_count(int(expect)),
50
+ except AssertionError as e:
51
+ raise AssertionError(f'实际={actual.count()}, 预期={expect}') from e
52
+ else:
53
+ raise MangoAutomationError(*ERROR_MSG_0021)
54
+
55
+ @async_method_callback('ass_web', '元素断言', 2, [
56
+ MethodModel(f='actual'),
57
+ MethodModel(f='expect', p='请输入不包含的文本', d=True)])
58
+ async def w_not_to_contain_text(self, actual: Locator, expect: str):
59
+ """元素不包含文本"""
60
+ try:
61
+ await exp(actual).not_to_contain_text(expect)
62
+ except AssertionError as e:
63
+ raise AssertionError(f'实际={actual}, 预期={expect}') from e
64
+
65
+ @async_method_callback('ass_web', '元素断言', 3, [MethodModel(f='actual')])
66
+ async def w_not_to_be_empty(self, actual: Locator):
67
+ """元素不为空"""
68
+ try:
69
+ await exp(actual).not_to_be_empty()
70
+ except AssertionError as e:
71
+ raise AssertionError(f'实际={actual}, 预期=不为空') from e
72
+
73
+ @async_method_callback('ass_web', '元素断言', 4, [MethodModel(f='actual')])
74
+ async def w_not_to_be_enabled(self, actual: Locator):
75
+ """元素不启用"""
76
+ try:
77
+ await exp(actual).not_to_be_enabled()
78
+ except AssertionError as e:
79
+ raise AssertionError(f'实际={actual}, 预期=不启用') from e
80
+
81
+ @async_method_callback('ass_web', '元素断言', 5, [MethodModel(f='actual')])
82
+ async def w_not_to_be_focused(self, actual: Locator):
83
+ """元素不聚焦"""
84
+ try:
85
+ await exp(actual).not_to_be_focused()
86
+ except AssertionError as e:
87
+ raise AssertionError(f'实际={actual}, 预期=不聚焦') from e
88
+
89
+ @async_method_callback('ass_web', '元素断言', 6, [MethodModel(f='actual')])
90
+ async def w_not_to_be_hidden(self, actual: Locator):
91
+ """元素不可隐藏"""
92
+ try:
93
+ await exp(actual).not_to_be_hidden()
94
+ except AssertionError as e:
95
+ raise AssertionError(f'实际={actual}, 预期=不可隐藏') from e
96
+
97
+ @async_method_callback('ass_web', '元素断言', 7, [MethodModel(f='actual')])
98
+ async def w_not_to_be_in_viewport(self, actual: Locator):
99
+ """元素不在视窗中"""
100
+ try:
101
+ await exp(actual).not_to_be_in_viewport()
102
+ except AssertionError as e:
103
+ raise AssertionError(f'实际={actual}, 预期=不在视窗中') from e
104
+
105
+ @async_method_callback('ass_web', '元素断言', 8, [MethodModel(f='actual')])
106
+ async def w_not_to_be_visible(self, actual: Locator):
107
+ """元素不可见"""
108
+ try:
109
+ await exp(actual).not_to_be_visible()
110
+ except AssertionError as e:
111
+ raise AssertionError(f'实际={actual}, 预期=不可见') from e
112
+
113
+ @async_method_callback('ass_web', '元素断言', 9, [
114
+ MethodModel(f='actual'),
115
+ MethodModel(f='expect', p='请输入样式', d=True)])
116
+ async def w_not_to_have_class(self, actual: Locator, expect: str):
117
+ """元素没有阶级"""
118
+ try:
119
+ await exp(actual).not_to_have_class(expect)
120
+ except AssertionError as e:
121
+ raise AssertionError(f'实际={actual}, 预期=没有阶级') from e
122
+
123
+ @async_method_callback('ass_web', '元素断言', 10, [MethodModel(f='actual')])
124
+ async def w_to_be_checked(self, actual: Locator):
125
+ """复选框已选中"""
126
+ try:
127
+ await exp(actual).to_be_checked()
128
+ except AssertionError as e:
129
+ raise AssertionError(f'实际={actual}, 预期=复选框已选中') from e
130
+
131
+ @async_method_callback('ass_web', '元素断言', 11, [MethodModel(f='actual')])
132
+ async def w_to_be_disabled(self, actual: Locator):
133
+ """元素已禁用"""
134
+ try:
135
+ await exp(actual).to_be_disabled()
136
+ except AssertionError as e:
137
+ raise AssertionError(f'实际={actual}, 预期=已禁用') from e
138
+
139
+ @async_method_callback('ass_web', '元素断言', 12, [MethodModel(f='actual')])
140
+ async def w_not_to_be_editable(self, actual: Locator):
141
+ """元素已启用"""
142
+ try:
143
+ await exp(actual).to_be_editable()
144
+ except AssertionError as e:
145
+ raise AssertionError(f'实际={actual}, 预期=已启用') from e
146
+
147
+ @async_method_callback('ass_web', '元素断言', 13, [MethodModel(f='actual')])
148
+ async def w_to_be_empty(self, actual: Locator | list | None):
149
+ """元素为空"""
150
+ if actual is None:
151
+ assert True, f'实际={actual}, 预期=为空'
152
+ else:
153
+ try:
154
+ await exp(actual).to_be_empty()
155
+ except AssertionError as e:
156
+ raise AssertionError(f'实际={actual}, 预期=为空') from e
157
+
158
+ @async_method_callback('ass_web', '元素断言', 14, [MethodModel(f='actual')])
159
+ async def w_to_be_visible(self, actual: Locator):
160
+ """元素可见"""
161
+ try:
162
+ await exp(actual).to_be_visible()
163
+ except AssertionError as e:
164
+ raise AssertionError(f'实际={actual}, 预期=可见') from e
165
+ # @staticmethod
166
+ # async def w_not_to_have_actuals(actual: Locator, actuals: list):
167
+ # """选择已选择选项"""
168
+ # await exp(actual).to_have_actuals(actuals)
169
+
170
+ # @staticmethod
171
+ # def w_not_to_have_attribute(locating: Locator, name: str, actual: str):
172
+ # """元素不具有属性"""
173
+ # exp(locating).not_to_have_attribute(name, actual)
174
+ # @staticmethod
175
+
176
+ # @staticmethod
177
+ # def w_not_to_have_css(locating: Locator, name: str, actual: str):
178
+ # """元素不使用CSS"""
179
+ # exp(locating).not_to_have_css(name, actual)
180
+
181
+ # @staticmethod
182
+ # def w_not_to_have_id(locating: Locator, _id: str):
183
+ # """元素没有ID"""
184
+ # exp(locating).not_to_have_id(_id)
185
+ #
186
+ # @staticmethod
187
+ # def w_not_to_have_js_property(locating: Locator, name: str, actual):
188
+ # """元素不具有js属性"""
189
+ # exp(locating).not_to_have_js_property(name, actual)
190
+ #
191
+ # @staticmethod
192
+ # def w_not_to_have_text(locating: Locator, expected: str):
193
+ # """元素没有文本"""
194
+ # exp(locating).not_to_have_text(expected)
195
+
196
+ # @staticmethod
197
+ # def w_not_to_have_actual(locating: Locator, actual: str):
198
+ # """元素无价值"""
199
+ # exp(locating).not_to_have_actual(actual)
200
+
201
+ #
202
+ # def w_to_be_attached(self, hidden_text: str):
203
+ # """待连接"""
204
+ # exp(self.page.get_by_text(hidden_text)).to_be_attached()
205
+
206
+ #
207
+ # def w_to_be_editable(self, hidden_text: str):
208
+ # """可编辑"""
209
+ # locator = self.page.get_by_role("textbox")
210
+ # exp(locator).to_be_editable()
211
+
212
+ # def w_to_be_enabled(self, hidden_text: str):
213
+ # """为空"""
214
+ # locator = self.page.locator("button.submit")
215
+ # exp(locator).to_be_enabled()
216
+
217
+ # def w_to_be_focused(self, hidden_text: str):
218
+ # """聚焦"""
219
+ # locator = self.page.get_by_role("textbox")
220
+ # exp(locator).to_be_focused()
221
+ #
222
+ # def w_to_be_hidden(self, hidden_text: str):
223
+ # """隐藏"""
224
+ # locator = self.page.locator('.my-element')
225
+ # exp(locator).to_be_hidden()
226
+ #
227
+ # def w_to_be_in_viewport(self, hidden_text: str):
228
+ # """待在视口中"""
229
+ # locator = self.page.get_by_role("button")
230
+ # # Make sure at least some part of element intersects viewport.
231
+ # exp(locator).to_be_in_viewport()
232
+ # # Make sure element is fully outside of viewport.
233
+ # exp(locator).not_to_be_in_viewport()
234
+ # # Make sure that at least half of the element intersects viewport.
235
+ # exp(locator).to_be_in_viewport(ratio=0.5)
236
+ #
237
+
238
+ # def w_to_contain_text(self, hidden_text: str):
239
+ # """包含文本"""
240
+ # locator = self.page.locator('.title')
241
+ # exp(locator).to_contain_text("substring")
242
+ # exp(locator).to_contain_text(re.compile(r"\d messages"))
243
+ #
244
+ # def w_to_have_attribute(self, hidden_text: str):
245
+ # """具有属性"""
246
+ # locator = self.page.locator("input")
247
+ # exp(locator).to_have_attribute("type", "text")
248
+ #
249
+ # def w_to_have_class(self, hidden_text: str):
250
+ # """到保存类别"""
251
+ # locator = self.page.locator("#component")
252
+ # exp(locator).to_have_class(re.compile(r"selected"))
253
+ # exp(locator).to_have_class("selected row")
254
+ #
255
+ # def w_to_have_count(self, hidden_text: str):
256
+ # """有计数"""
257
+ # locator = self.page.locator("list > .component")
258
+ # exp(locator).to_have_count(3)
259
+ #
260
+ # def w_to_have_css(self, hidden_text: str):
261
+ # """使用CSS"""
262
+ # locator = self.page.get_by_role("button")
263
+ # exp(locator).to_have_css("display", "flex")
264
+ #
265
+ # def w_to_have_id(self, hidden_text: str):
266
+ # """到id"""
267
+ # locator = self.page.get_by_role("textbox")
268
+ # exp(locator).to_have_id("lastname")
269
+ #
270
+ # def w_to_have_js_property(self, hidden_text: str):
271
+ # """拥有js属性"""
272
+ # locator = self.page.locator(".component")
273
+ # exp(locator).to_have_js_property("loaded", True)
274
+ #
275
+ # def w_to_have_text(self, hidden_text: str):
276
+ # """有文本"""
277
+ # locator = self.page.locator(".title")
278
+ # exp(locator).to_have_text(re.compile(r"Welcome, Test User"))
279
+ # exp(locator).to_have_text(re.compile(r"Welcome, .*"))
280
+ #
281
+ # def w_to_have_actual(self, hidden_text: str):
282
+ # """有价值"""
283
+ # locator = self.page.locator("input[type=number]")
284
+ # exp(locator).to_have_actual(re.compile(r"[0-9]"))
285
+
286
+
287
+ if __name__ == '__main__':
288
+ from mangokit.decorator import func_info
289
+
290
+ print(func_info)
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description: # @Time : 2023-04-25 22:33
4
+ # @Author : 毛鹏
5
+ import asyncio
6
+ import json
7
+ import os.path
8
+ import traceback
9
+ from urllib.parse import urlparse
10
+
11
+ from playwright._impl._errors import TimeoutError, Error
12
+
13
+ from mangotools.decorator import async_method_callback
14
+ from mangotools.models import MethodModel
15
+ from ....exceptions import MangoAutomationError
16
+ from ....exceptions._error_msg import ERROR_MSG_0049, ERROR_MSG_0013, ERROR_MSG_0058, ERROR_MSG_0059
17
+ from ....tools import Meta
18
+ from ..._base_data import BaseData
19
+
20
+
21
+ class AsyncWebBrowser(metaclass=Meta):
22
+ """浏览器操作"""
23
+
24
+ def __init__(self, base_data: BaseData):
25
+ self.base_data = base_data
26
+
27
+ @async_method_callback('web', '浏览器操作', 0, [
28
+ MethodModel(f='_time', p='请输入等待时间', d=True)])
29
+ async def w_wait_for_timeout(self, _time: int):
30
+ """强制等待"""
31
+ await asyncio.sleep(int(_time))
32
+
33
+ @async_method_callback('web', '浏览器操作', 1, [
34
+ MethodModel(f='url', p='请输入URL', d=True)])
35
+ async def w_goto(self, url: str):
36
+ """打开URL"""
37
+ try:
38
+ result = urlparse(url)
39
+ if not all([result.scheme, result.netloc]):
40
+ raise MangoAutomationError(*ERROR_MSG_0049)
41
+ await self.base_data.page.goto(url, timeout=60000)
42
+ await asyncio.sleep(2)
43
+ except TimeoutError as error:
44
+ self.base_data.log.debug(f'打开URL失败-2,类型:{type(error)},失败详情:{error}')
45
+ raise MangoAutomationError(*ERROR_MSG_0013, value=(url,))
46
+ except Error as error:
47
+ self.base_data.log.debug(
48
+ f'打开URL失败-2,类型:{type(error)},失败详情:{error},失败明细:{traceback.format_exc()}')
49
+ raise MangoAutomationError(*ERROR_MSG_0058, value=(url,))
50
+
51
+ @async_method_callback('web', '浏览器操作', 2, [
52
+ MethodModel(f='path', p='请输入截图保存路径', d=True)])
53
+ async def w_screenshot(self, path: str):
54
+ """整个页面截图"""
55
+ await self.base_data.page.screenshot(path=path, full_page=True)
56
+
57
+ @async_method_callback('web', '浏览器操作', 3)
58
+ async def w_alert(self):
59
+ """设置弹窗不予处理"""
60
+ self.base_data.page.on("dialog", lambda dialog: dialog.accept())
61
+
62
+ @async_method_callback('web', '浏览器操作', 4)
63
+ async def w_get_cookie(self):
64
+ """获取cookie"""
65
+ with open(os.path.join(self.base_data.download_path, 'storage_state.json'), 'w') as file:
66
+ file.write(json.dumps(await self.base_data.context.storage_state()))
67
+
68
+ @async_method_callback('web', '浏览器操作', 5, [
69
+ MethodModel(f='storage_state', p='请输入获取cookie方法中获取的内容', d=True)])
70
+ async def w_set_cookie(self, storage_state: str):
71
+ """设置cookie"""
72
+ if isinstance(storage_state, str):
73
+ storage_state = json.loads(storage_state)
74
+ else:
75
+ raise MangoAutomationError(*ERROR_MSG_0059)
76
+ await self.base_data.context.add_cookies(storage_state['cookies'])
77
+ for storage in storage_state['origins']:
78
+ local_storage = storage.get('localStorage', [])
79
+ session_storage = storage.get('sessionStorage', [])
80
+ for item in local_storage:
81
+ await self.base_data.context.add_init_script(
82
+ f"window.localStorage.setItem('{item['name']}', '{item['value']}');")
83
+ for item in session_storage:
84
+ await self.base_data.context.add_init_script(
85
+ f"window.sessionStorage.setItem('{item['name']}', '{item['value']}');")
86
+ await self.base_data.page.reload()
87
+
88
+ @async_method_callback('web', '浏览器操作', 6)
89
+ async def w_clear_cookies(self):
90
+ """清除所有cookie"""
91
+ await self.base_data.context.clear_cookies()
92
+
93
+ @async_method_callback('web', '浏览器操作', 7)
94
+ async def w_clear_storage(self):
95
+ """清除本地存储和会话存储"""
96
+ await self.base_data.page.evaluate("() => localStorage.clear()")
97
+ await self.base_data.page.evaluate("() => sessionStorage.clear()")
@@ -0,0 +1,14 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Project: 芒果测试平台
3
+ # @Description: # @Time : 2023-04-26 22:22
4
+ # @Author : 毛鹏
5
+
6
+ from ....tools import Meta
7
+ from ..._base_data import BaseData
8
+
9
+
10
+ class AsyncWebCustomization(metaclass=Meta):
11
+ """定制开发"""
12
+
13
+ def __init__(self, base_data: BaseData):
14
+ self.base_data = base_data