kyapp 0.0.2__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.
Files changed (62) hide show
  1. kyapp-0.0.2/PKG-INFO +30 -0
  2. kyapp-0.0.2/demo/config.py +4 -0
  3. kyapp-0.0.2/demo/pages/__init__.py +4 -0
  4. kyapp-0.0.2/demo/pages/home_page.py +9 -0
  5. kyapp-0.0.2/demo/pages/login_page.py +68 -0
  6. kyapp-0.0.2/demo/run.py +17 -0
  7. kyapp-0.0.2/demo/tests/__init__.py +0 -0
  8. kyapp-0.0.2/demo/tests/test_go_me.py +23 -0
  9. kyapp-0.0.2/kyapp/__init__.py +13 -0
  10. kyapp-0.0.2/kyapp/case.py +100 -0
  11. kyapp-0.0.2/kyapp/cli.py +41 -0
  12. kyapp-0.0.2/kyapp/core/__init__.py +4 -0
  13. kyapp-0.0.2/kyapp/core/adr/__init__.py +13 -0
  14. kyapp-0.0.2/kyapp/core/adr/case.py +108 -0
  15. kyapp-0.0.2/kyapp/core/adr/driver.py +154 -0
  16. kyapp-0.0.2/kyapp/core/adr/element.py +291 -0
  17. kyapp-0.0.2/kyapp/core/adr/page.py +12 -0
  18. kyapp-0.0.2/kyapp/core/adr/remote_adb.py +73 -0
  19. kyapp-0.0.2/kyapp/core/adr/remote_driver.py +179 -0
  20. kyapp-0.0.2/kyapp/core/img/__init__.py +4 -0
  21. kyapp-0.0.2/kyapp/core/img/driver.py +43 -0
  22. kyapp-0.0.2/kyapp/core/img/element.py +66 -0
  23. kyapp-0.0.2/kyapp/core/ios/__init__.py +15 -0
  24. kyapp-0.0.2/kyapp/core/ios/case.py +117 -0
  25. kyapp-0.0.2/kyapp/core/ios/driver.py +193 -0
  26. kyapp-0.0.2/kyapp/core/ios/elem.py +221 -0
  27. kyapp-0.0.2/kyapp/core/ios/local_wda.py +113 -0
  28. kyapp-0.0.2/kyapp/core/ios/page.py +11 -0
  29. kyapp-0.0.2/kyapp/core/ios/remote_driver.py +220 -0
  30. kyapp-0.0.2/kyapp/core/ios/remote_wda.py +58 -0
  31. kyapp-0.0.2/kyapp/core/ocr/__init__.py +9 -0
  32. kyapp-0.0.2/kyapp/core/ocr/driver.py +63 -0
  33. kyapp-0.0.2/kyapp/core/ocr/element.py +62 -0
  34. kyapp-0.0.2/kyapp/element.py +118 -0
  35. kyapp-0.0.2/kyapp/page.py +12 -0
  36. kyapp-0.0.2/kyapp/running/__init__.py +0 -0
  37. kyapp-0.0.2/kyapp/running/conf.py +12 -0
  38. kyapp-0.0.2/kyapp/running/conf.yml +19 -0
  39. kyapp-0.0.2/kyapp/running/runner.py +49 -0
  40. kyapp-0.0.2/kyapp/utils/__init__.py +1 -0
  41. kyapp-0.0.2/kyapp/utils/aes_util.py +49 -0
  42. kyapp-0.0.2/kyapp/utils/allure_util.py +213 -0
  43. kyapp-0.0.2/kyapp/utils/base64_util.py +25 -0
  44. kyapp-0.0.2/kyapp/utils/common.py +53 -0
  45. kyapp-0.0.2/kyapp/utils/config.py +135 -0
  46. kyapp-0.0.2/kyapp/utils/cv_util.py +114 -0
  47. kyapp-0.0.2/kyapp/utils/dingtalk.py +56 -0
  48. kyapp-0.0.2/kyapp/utils/excel_util.py +103 -0
  49. kyapp-0.0.2/kyapp/utils/log.py +12 -0
  50. kyapp-0.0.2/kyapp/utils/mail.py +47 -0
  51. kyapp-0.0.2/kyapp/utils/mysql_util.py +76 -0
  52. kyapp-0.0.2/kyapp/utils/nacos_util.py +54 -0
  53. kyapp-0.0.2/kyapp/utils/pytest_util.py +89 -0
  54. kyapp-0.0.2/kyapp/utils/swagger_util.py +197 -0
  55. kyapp-0.0.2/kyapp.egg-info/PKG-INFO +30 -0
  56. kyapp-0.0.2/kyapp.egg-info/SOURCES.txt +60 -0
  57. kyapp-0.0.2/kyapp.egg-info/dependency_links.txt +1 -0
  58. kyapp-0.0.2/kyapp.egg-info/entry_points.txt +2 -0
  59. kyapp-0.0.2/kyapp.egg-info/requires.txt +12 -0
  60. kyapp-0.0.2/kyapp.egg-info/top_level.txt +1 -0
  61. kyapp-0.0.2/setup.cfg +4 -0
  62. kyapp-0.0.2/setup.py +48 -0
kyapp-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,30 @@
1
+ Metadata-Version: 2.4
2
+ Name: kyapp
3
+ Version: 0.0.2
4
+ Summary: 安卓/IOS自动化测试框架
5
+ Home-page: https://gitee.com/bluepang2021/kyapp_project
6
+ Author: 杨康
7
+ Author-email: 772840356@qq.com
8
+ Platform: Android/IOS
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: click==8.1.7
12
+ Requires-Dist: loguru==0.7.0
13
+ Requires-Dist: PyYAML==6.0.1
14
+ Requires-Dist: allure-pytest==2.9.45
15
+ Requires-Dist: pytest==6.2.5
16
+ Requires-Dist: pytest-xdist==2.5.0
17
+ Requires-Dist: pytest-rerunfailures==10.2
18
+ Requires-Dist: pytest-repeat==0.9.3
19
+ Requires-Dist: uiviewer==1.1.3
20
+ Requires-Dist: uiautomator2==3.2.4
21
+ Requires-Dist: tidevice==0.12.10
22
+ Requires-Dist: facebook-wda==1.5.0
23
+ Dynamic: author
24
+ Dynamic: author-email
25
+ Dynamic: classifier
26
+ Dynamic: home-page
27
+ Dynamic: platform
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
@@ -0,0 +1,4 @@
1
+ class Config:
2
+ EMAIL = 'lute2026@qq.com'
3
+ PASSWORD = 'lute123456'
4
+
@@ -0,0 +1,4 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2024/9/14 09:43
4
+ """
@@ -0,0 +1,9 @@
1
+ from kyapp import Elem, Page
2
+
3
+
4
+ class HomePage(Page):
5
+ me_tab = Elem(
6
+ title='底部我的tab入口',
7
+ adr=('resourceId', 'com.lute.momcozy:id/i_me'),
8
+ ios=('label', 'Me')
9
+ )
@@ -0,0 +1,68 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2024/9/14 09:44
4
+ """
5
+ from config import Config
6
+ from kyapp import Page, Elem, logger
7
+ from pages.home_page import HomePage
8
+
9
+
10
+ class LoginPage(Page):
11
+ reselect_ok = Elem(
12
+ title='地区偏移提示',
13
+ adr=('resourceId', 'com.lute.momcozy:id/btnNegative'),
14
+ ios=('label', 'OK')
15
+ )
16
+ welcome_text = Elem(
17
+ title='欢迎语',
18
+ adr=('text', 'Welcome to Momcozy'),
19
+ ios=('text', 'Welcome to Momcozy')
20
+ )
21
+ account_input = Elem(
22
+ title='账号输入框',
23
+ adr=('resourceId', 'com.lute.momcozy:id/etAccount'),
24
+ ios=('className', 'TextField'),
25
+ )
26
+ pwd_input = Elem(
27
+ title='密码输入框',
28
+ adr=('resourceId', 'com.lute.momcozy:id/etPassword'),
29
+ ios=('className', 'SecureTextField')
30
+ )
31
+ login_btn = Elem(
32
+ title='登录按钮',
33
+ adr=('resourceId', 'com.lute.momcozy:id/btnLogin'),
34
+ ios=('label', 'Log In'),
35
+ )
36
+
37
+
38
+ class LoginApi:
39
+
40
+ def __init__(self, driver):
41
+ self.driver = driver
42
+ self.login_page = LoginPage(self.driver)
43
+ self.home_page = HomePage(self.driver)
44
+
45
+ def login_status_check(self, timeout=5):
46
+ if self.home_page.me_tab.exists(timeout=timeout):
47
+ return True
48
+ return False
49
+
50
+ def country_change_dialog_check(self):
51
+ if self.login_page.reselect_ok.exists():
52
+ self.login_page.reselect_ok.click()
53
+
54
+ def pwd_login(self):
55
+ self.login_page.account_input.clear()
56
+ self.login_page.account_input.input(Config.EMAIL)
57
+ self.login_page.pwd_input.input(Config.PASSWORD)
58
+ self.login_page.welcome_text.click()
59
+ self.country_change_dialog_check()
60
+ self.login_page.login_btn.click()
61
+
62
+ def login(self):
63
+ if self.login_status_check():
64
+ logger.info('已登录')
65
+ else:
66
+ logger.info('未登录,进行登录')
67
+ self.pwd_login()
68
+ assert self.login_status_check(timeout=30)
@@ -0,0 +1,17 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2024/7/12 17:10
4
+ """
5
+ import kyapp
6
+
7
+
8
+ if __name__ == '__main__':
9
+ kyapp.main(
10
+ case_path='tests',
11
+ did='3B661X0181J00000',
12
+ pkg='com.lute.momcozy',
13
+ platform='adr'
14
+ )
15
+
16
+
17
+
File without changes
@@ -0,0 +1,23 @@
1
+ import kyapp
2
+ from pages.login_page import LoginApi
3
+ from pages.home_page import HomePage
4
+
5
+ class TestAdrDemo(kyapp.TestCase):
6
+ def start(self):
7
+ self.start_app(force=True)
8
+ LoginApi(self.dr).login()
9
+ self.home_page = HomePage(self.dr)
10
+
11
+ def test_go_me(self):
12
+ self.sleep(10)
13
+ self.home_page.me_tab.click()
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
@@ -0,0 +1,13 @@
1
+ from .running.runner import main
2
+ from .running.conf import App
3
+ from .utils.config import FileConfig
4
+ from .utils.pytest_util import *
5
+ from .utils.allure_util import *
6
+ from .utils.log import logger
7
+ from .page import Page
8
+ from .element import Elem
9
+ from .case import TestCase
10
+
11
+
12
+ __version__ = "0.0.2"
13
+ __description__ = "安卓/IOS自动化测试框架"
@@ -0,0 +1,100 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2023/10/26 09:48
4
+ """
5
+ import time
6
+
7
+ from .core.adr.driver import Driver as AdrDriver
8
+ from .core.ios.driver import Driver as IosDriver
9
+
10
+ from .running.conf import App
11
+ from .utils.log import logger
12
+
13
+
14
+ class TestCase:
15
+ """
16
+ 测试用例基类,所有测试用例需要继承该类
17
+ """
18
+
19
+ dr = None
20
+
21
+ # ---------------------初始化-------------------------------
22
+ def start_class(self):
23
+ """
24
+ Hook method for setup_class fixture
25
+ :return:
26
+ """
27
+ pass
28
+
29
+ def end_class(self):
30
+ """
31
+ Hook method for teardown_class fixture
32
+ :return:
33
+ """
34
+ pass
35
+
36
+ @classmethod
37
+ def setup_class(cls):
38
+ try:
39
+ cls().start_class()
40
+ except BaseException as e:
41
+ logger.error(f"start_class Exception: {e}")
42
+
43
+ @classmethod
44
+ def teardown_class(cls):
45
+ try:
46
+ cls().end_class()
47
+ except BaseException as e:
48
+ logger.error(f"end_class Exception: {e}")
49
+
50
+ def start(self):
51
+ """
52
+ Hook method for setup_method fixture
53
+ :return:
54
+ """
55
+ pass
56
+
57
+ def end(self):
58
+ """
59
+ Hook method for teardown_method fixture
60
+ :return:
61
+ """
62
+ pass
63
+
64
+ def setup_method(self):
65
+ did = App.did
66
+ pkg = App.pkg
67
+ if App.platform == 'ios':
68
+ self.dr = IosDriver(udid=did, bundle_id=pkg)
69
+ else:
70
+ self.dr = AdrDriver(serial=did, package=pkg)
71
+
72
+ self.start()
73
+
74
+ def teardown_method(self):
75
+ self.end()
76
+ if App.platform == 'ios':
77
+ self.dr.close()
78
+ print('休眠5s,防止释放设备过慢的情况!')
79
+ self.sleep(5)
80
+
81
+ # 公共方法
82
+ @staticmethod
83
+ def sleep(n: float):
84
+ logger.info(f"暂停: {n}s")
85
+ time.sleep(n)
86
+
87
+ def shot(self, name: str, delay=0):
88
+ if delay:
89
+ self.sleep(delay)
90
+ self.dr.shot(name)
91
+
92
+ def start_app(self, force=True):
93
+ if force:
94
+ self.stop_app()
95
+ self.dr.start_app()
96
+
97
+ def stop_app(self):
98
+ self.dr.stop_app()
99
+
100
+
@@ -0,0 +1,41 @@
1
+ import click
2
+ from . import __version__
3
+
4
+ @click.group()
5
+ @click.version_option(version=__version__, help="Show version.")
6
+ # 老是变,等最后定下来再搞,目前也没啥用
7
+ def main():
8
+ pass
9
+
10
+
11
+ # @main.command()
12
+ # @click.option('-p', '--platform', help="Specify the platform.")
13
+ # def create(platform):
14
+ # """
15
+ # 创建新项目,老是变,先注释掉
16
+ # @param platform: 平台,如api、android、ios、web
17
+ # @return:
18
+ # """
19
+ # create_scaffold(platform)
20
+
21
+
22
+ # @main.command()
23
+ # @click.option('-p', '--platform', help="Specify the platform.")
24
+ # @click.option('-u', '--url', help="Specify the url.")
25
+ # def inspector(platform, url):
26
+ # """
27
+ # 获取元素定位信息
28
+ # @param platform: 平台,android、ios、web
29
+ # @param url:针对web,需要录制的页面的url
30
+ # @return:
31
+ # """
32
+ # import os
33
+ # if platform in ['android', 'ios', 'web']:
34
+ # os.system('weditor')
35
+ # elif platform == 'web':
36
+ # if not url:
37
+ # raise KeyError('url不能为空')
38
+ # os.system(f'playwright codegen {url}')
39
+ # else:
40
+ # raise KeyError('只支持android、ios、web')
41
+
@@ -0,0 +1,4 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2025/4/8 09:37
4
+ """
@@ -0,0 +1,13 @@
1
+ from .element import Elem
2
+ from .page import Page
3
+ from .driver import Driver
4
+ from .remote_driver import RemoteDriver
5
+ from .case import TestCase
6
+
7
+ __all__ = [
8
+ "Elem",
9
+ "Page",
10
+ "Driver",
11
+ "TestCase",
12
+ "RemoteDriver"
13
+ ]
@@ -0,0 +1,108 @@
1
+ """
2
+ @Author: kang.yang
3
+ @Date: 2023/10/26 09:48
4
+ """
5
+ import time
6
+
7
+ from .driver import Driver
8
+ from .remote_driver import RemoteDriver
9
+
10
+ from kyapp.running.conf import App
11
+ from kyapp.utils.log import logger
12
+
13
+ class TestCase:
14
+ """
15
+ 测试用例基类,所有测试用例需要继承该类
16
+ """
17
+
18
+ dr = None
19
+
20
+ # ---------------------初始化-------------------------------
21
+ def start_class(self):
22
+ """
23
+ Hook method for setup_class fixture
24
+ :return:
25
+ """
26
+ pass
27
+
28
+ def end_class(self):
29
+ """
30
+ Hook method for teardown_class fixture
31
+ :return:
32
+ """
33
+ pass
34
+
35
+ @classmethod
36
+ def setup_class(cls):
37
+ try:
38
+ cls().start_class()
39
+ except BaseException as e:
40
+ logger.error(f"start_class Exception: {e}")
41
+
42
+ @classmethod
43
+ def teardown_class(cls):
44
+ try:
45
+ cls().end_class()
46
+ except BaseException as e:
47
+ logger.error(f"end_class Exception: {e}")
48
+
49
+ def start(self):
50
+ """
51
+ Hook method for setup_method fixture
52
+ :return:
53
+ """
54
+ pass
55
+
56
+ def end(self):
57
+ """
58
+ Hook method for teardown_method fixture
59
+ :return:
60
+ """
61
+ pass
62
+
63
+ def setup_method(self):
64
+
65
+ # 驱动初始化
66
+ if App.sonic_host is not None:
67
+ self.dr = RemoteDriver(
68
+ serial=App.did,
69
+ package=App.pkg,
70
+ sonic_host=App.sonic_host,
71
+ sonic_user=App.sonic_user,
72
+ sonic_pwd=App.sonic_pwd
73
+ )
74
+ else:
75
+ self.dr = Driver(
76
+ serial=App.did,
77
+ package=App.pkg
78
+ )
79
+ # 解锁设备
80
+ self.dr.unlock()
81
+
82
+ self.start()
83
+
84
+ def teardown_method(self):
85
+ self.end()
86
+ if isinstance(self.dr, RemoteDriver):
87
+ self.dr.close()
88
+
89
+ # 公共方法
90
+ @staticmethod
91
+ def sleep(n: float):
92
+ logger.info(f"暂停: {n}s")
93
+ time.sleep(n)
94
+
95
+ def shot(self, name: str, delay=0):
96
+ if delay:
97
+ self.sleep(delay)
98
+ self.dr.shot(name)
99
+
100
+ def start_app(self, force=True):
101
+ if force:
102
+ self.stop_app()
103
+ self.dr.start_app()
104
+
105
+ def stop_app(self):
106
+ self.dr.stop_app()
107
+
108
+
@@ -0,0 +1,154 @@
1
+ import allure
2
+ import uiautomator2 as u2
3
+
4
+ from kyapp.utils.log import logger
5
+ from kyapp.utils.common import general_file_path
6
+
7
+
8
+ class Driver:
9
+ """
10
+ u2库代码地址:https://github.com/openatx/uiautomator2
11
+ """
12
+
13
+ def __init__(self, serial: str, package: str = None):
14
+ """
15
+ @param serial: 设备序列号,通过adb devices获取,或者通过关于手机-序列号获取
16
+ @param package: 应用包名,通过adb shell pm list packages获取
17
+ """
18
+ logger.info(f"初始化本地设备连接: {serial}")
19
+ self.package = package
20
+ self.d = u2.connect(serial)
21
+
22
+ def uninstall_app(self, package: str = None):
23
+ """
24
+ 卸载应用
25
+ @param package: 应用包名
26
+ @return:
27
+ """
28
+ if self.package is None:
29
+ if package is None:
30
+ raise KeyError('package不能为空')
31
+ else:
32
+ self.package = package
33
+ logger.info(f"卸载应用: {self.package}")
34
+ self.d.app_uninstall(self.package)
35
+
36
+ def install_app(self, apk_url, package: str = None):
37
+ """
38
+ 安装应用
39
+ @param apk_url: apk链接
40
+ @param package: 应用包名
41
+ @return:
42
+ """
43
+ if self.package is None:
44
+ if package is None:
45
+ raise KeyError('package不能为空')
46
+ else:
47
+ self.package = package
48
+ self.d.app_uninstall(self.package)
49
+ logger.info(f"安装应用: {apk_url}")
50
+ self.d.app_install(apk_url)
51
+
52
+ def start_app(self, package: str = None):
53
+ """
54
+ 启动应用
55
+ @param package: 应用包名
56
+ @return:
57
+ """
58
+ if self.package is None:
59
+ if package is None:
60
+ raise KeyError('package不能为空')
61
+ else:
62
+ self.package = package
63
+ logger.info(f"启动应用: {self.package}")
64
+ self.d.app_start(package_name=self.package, use_monkey=True)
65
+
66
+ def stop_app(self, package: str = None):
67
+ """
68
+ 停止应用
69
+ @param package: 应用包名
70
+ @return:
71
+ """
72
+ if self.package is None:
73
+ if package is None:
74
+ raise KeyError('package不能为空')
75
+ else:
76
+ self.package = package
77
+ logger.info(f"停止应用: {self.package}")
78
+ self.d.app_stop(self.package)
79
+
80
+ def shot(self, file_name: str = None):
81
+ """
82
+ 截图
83
+ @param file_name: 截图保存后的文件名
84
+ @return:
85
+ """
86
+ file_path = general_file_path(file_name)
87
+ logger.info(f"截图保存至: {file_path}")
88
+ self.d.screenshot(file_path)
89
+
90
+ logger.info("截图上传allure报告")
91
+ allure.attach.file(
92
+ file_path,
93
+ attachment_type=allure.attachment_type.PNG,
94
+ name=f"{file_path}",
95
+ )
96
+ return file_path
97
+
98
+ def click(self, x, y):
99
+ """
100
+ 点击坐标
101
+ @param x: 横坐标
102
+ @param y: 纵坐标
103
+ @return:
104
+ """
105
+ self.d.click(x, y)
106
+
107
+ def input(self, *args, **kwargs):
108
+ """
109
+ 输入
110
+ @param args:
111
+ @param kwargs:
112
+ @return:
113
+ """
114
+ self.d.send_keys(*args, **kwargs)
115
+
116
+ def unlock(self):
117
+ """
118
+ 设备解锁
119
+ @return:
120
+ """
121
+ self.d.unlock()
122
+
123
+ def shell(self, *args, **kwargs):
124
+ """
125
+ 执行adb shell
126
+ @param args:
127
+ @param kwargs:
128
+ @return:
129
+ """
130
+ self.d.shell(*args, **kwargs)
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+