├── .idea
├── appium.iml
├── encodings.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── Base
├── BaseAdb.py
├── BaseAndroidPhone.py
├── BaseApk.py
├── BaseAppiumServer.py
├── BaseConfig.py
├── BaseElementEnmu.py
├── BaseEmail.py
├── BaseError.py
├── BaseExcel.py
├── BaseFile.py
├── BaseInit.py
├── BaseIosCommand.py
├── BaseLog.py
├── BaseOperate.py
├── BasePickle.py
├── BaseRunner.py
├── BaseStatistics.py
├── BaseWebServer.py
├── BaseYaml.py
├── HTMLTestRunner.py
├── __init__.py
└── __pycache__
│ ├── BaseAdb.cpython-34.pyc
│ ├── BaseAndroidPhone.cpython-34.pyc
│ ├── BaseApk.cpython-34.pyc
│ ├── BaseAppiumServer.cpython-34.pyc
│ ├── BaseConfig.cpython-34.pyc
│ ├── BaseElementEnmu.cpython-34.pyc
│ ├── BaseError.cpython-34.pyc
│ ├── BaseExcel.cpython-34.pyc
│ ├── BaseFile.cpython-34.pyc
│ ├── BaseInit.cpython-34.pyc
│ ├── BaseLog.cpython-34.pyc
│ ├── BaseOperate.cpython-34.pyc
│ ├── BasePickle.cpython-34.pyc
│ ├── BaseRunner.cpython-34.pyc
│ ├── BaseStatistics.cpython-34.pyc
│ ├── BaseYaml.cpython-34.pyc
│ └── __init__.cpython-34.pyc
├── CHANGELOG.md
├── Img
├── console.jpg
├── detail.jpg
└── sum.png
├── Log
└── sum.pickle
├── PageObject
├── Home
│ ├── CardsSortPage.py
│ ├── CollectSwipeDellPage.py
│ ├── FirstOpenPage.py
│ ├── HistorySwipeDellPage.py
│ ├── __init__.py
│ └── __pycache__
│ │ ├── FirstOpenPage.cpython-34.pyc
│ │ └── __init__.cpython-34.pyc
├── Pages.py
├── SumResult.py
├── __init__.py
└── __pycache__
│ ├── Pages.cpython-34.pyc
│ ├── SumResult.cpython-34.pyc
│ └── __init__.cpython-34.pyc
├── README.md
├── Report
└── Report.xlsx
├── Runner
├── __init__.py
├── runner.py
└── runner_ios.py
├── TestCase
├── HomeTest.py
├── __init__.py
└── __pycache__
│ ├── HomeTest.cpython-34.pyc
│ └── __init__.cpython-34.pyc
├── app
├── android-system-webview-60.apk
├── appium-uiautomator2-server-debug-androidTest.apk
├── appium-uiautomator2-server-v0.1.9.apk
└── kill5037.bat
├── doc
├── Appium_Android.docx
├── Appium_IOS.docx
├── ~$pium_Android.docx
└── 自动化框架使用说明.docx
├── mark.md
├── use.md
└── yamls
└── home
└── firstOpen.yaml
/.idea/appium.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | -------runnerCaseApp
97 | ----operate----
98 | operate1
99 | operate
100 | cts
101 | ---
102 | devices_Pool
103 | send_keys
104 | setUpModule
105 | activi
106 | datetime
107 | --
108 | date
109 | self.data
110 | detail
111 | read
112 | --------- Element.INFO--------
113 | iv_write
114 | ------------
115 | tv_publish
116 | elements_by
117 | webview
118 | runnerPool
119 | com.baiji.jianshu.account.SplashScreenActivity
120 | -------------
121 | aat
122 | Log
123 | /mcloud/mag/ProxyForText/knowledge/app/v5/findcards/cn
124 | packagename
125 | com.ximalaya.ting.android.host.activity.WelComeActivity
126 |
127 |
128 | driver
129 | data
130 | XXX
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 | Internationalization issues
213 |
214 |
215 | Python
216 |
217 |
218 |
219 |
220 | PyTypeCheckerInspection
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 | 1495681351908
700 |
701 |
702 | 1495681351908
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 | No facets are configured
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 | 1.8
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 | appium
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
--------------------------------------------------------------------------------
/Base/BaseAdb.py:
--------------------------------------------------------------------------------
1 |
2 | # -*- coding: utf-8 -*-
3 |
4 | import subprocess
5 |
6 | import os
7 | import random
8 |
9 |
10 | class AndroidDebugBridge(object):
11 | def call_adb(self, command):
12 | command_result = ''
13 | command_text = 'adb %s' % command
14 | # print(command_text)
15 | results = os.popen(command_text, "r")
16 | while 1:
17 | line = results.readline()
18 | if not line: break
19 | command_result += line
20 | results.close()
21 | return command_result
22 |
23 | # check for any fastboot device
24 | def fastboot(self, device_id):
25 | pass
26 |
27 | # 检查设备
28 | def attached_devices(self):
29 | # result = self.call_adb("devices")
30 | devices = []
31 | result = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE,
32 | stderr=subprocess.PIPE).stdout.readlines()
33 |
34 | for item in result:
35 | t = item.decode().split("\tdevice")
36 | if len(t) >= 2:
37 | devices.append(t[0])
38 | # print(result)
39 | # print(devices)
40 | return devices
41 | # 状态
42 | def get_state(self):
43 | result = self.call_adb("get-state")
44 | result = result.strip(' \t\n\r')
45 | return result or None
46 | #重启
47 | def reboot(self, option):
48 | command = "reboot"
49 | if len(option) > 7 and option in ("bootloader", "recovery",):
50 | command = "%s %s" % (command, option.strip())
51 | self.call_adb(command)
52 |
53 | # 将电脑文件拷贝到手机里面
54 | def push(self, local, remote):
55 | result = self.call_adb("push %s %s" % (local, remote))
56 | return result
57 |
58 | # 拉数据到本地
59 | def pull(self, remote, local):
60 | result = self.call_adb("pull %s %s" % (remote, local))
61 | return result
62 | # 同步更新 很少用此命名
63 | def sync(self, directory, **kwargs):
64 | command = "sync %s" % directory
65 | if 'list' in kwargs:
66 | command += " -l"
67 | result = self.call_adb(command)
68 | return result
69 |
70 | # 打开指定app
71 | def open_app(self,packagename,activity):
72 | result = self.call_adb("shell am start -n %s/%s" % (packagename, activity))
73 | check = result.partition('\n')[2].replace('\n', '').split('\t ')
74 | if check[0].find("Error") >= 1:
75 | return False
76 | else:
77 | return True
78 |
79 | # 根据包名得到进程id
80 | def get_app_pid(self, pkg_name):
81 | string = self.call_adb("shell ps | grep "+pkg_name)
82 | # print(string)
83 | if string == '':
84 | return "the process doesn't exist."
85 | result = string.split(" ")
86 | # print(result[4])
87 | return result[4]
88 |
89 | if __name__ == '__main__':
90 |
91 | # reuslt = AndroidDebugBridge().attached_devices()
92 | # print(reuslt)
93 |
94 | print(os.popen("adb devices", 'r').readline())
--------------------------------------------------------------------------------
/Base/BaseAndroidPhone.py:
--------------------------------------------------------------------------------
1 |
2 | # -*- coding: utf-8 -*-
3 | __author__ = 'shikun'
4 | import os
5 | import re
6 | import math
7 | from math import ceil
8 | import subprocess
9 | # 得到手机信息
10 | def getPhoneInfo(devices):
11 | cmd = "adb -s " + devices +" shell cat /system/build.prop "
12 | print(cmd)
13 | # phone_info = os.popen(cmd).readlines()
14 | phone_info = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.readlines()
15 | result = {"release": "5.0", "model": "model2", "brand": "brand1", "device": "device1"}
16 | release = "ro.build.version.release=" # 版本
17 | model = "ro.product.model=" #型号
18 | brand = "ro.product.brand=" # 品牌
19 | device = "ro.product.device=" # 设备名
20 | for line in phone_info:
21 | for i in line.split():
22 | temp = i.decode()
23 | if temp.find(release) >= 0:
24 | result["release"] = temp[len(release):]
25 | break
26 | if temp.find(model) >= 0:
27 | result["model"] = temp[len(model):]
28 | break
29 | if temp.find(brand) >= 0:
30 | result["brand"] = temp[len(brand):]
31 | break
32 | if temp.find(device) >= 0:
33 | result["device"] = temp[len(device) :]
34 | break
35 | print(result)
36 | return result
37 |
38 | # 得到最大运行内存
39 | def get_men_total(devices):
40 | cmd = "adb -s "+devices+ " shell cat /proc/meminfo"
41 | get_cmd = os.popen(cmd).readlines()
42 | men_total = 0
43 | men_total_str = "MemTotal"
44 | for line in get_cmd:
45 | if line.find(men_total_str) >= 0:
46 | men_total = line[len(men_total_str) +1:].replace("kB", "").strip()
47 | break
48 | return int(men_total)
49 | # 得到几核cpu
50 | def get_cpu_kel(devices):
51 | cmd = "adb -s " +devices +" shell cat /proc/cpuinfo"
52 | get_cmd = os.popen(cmd).readlines()
53 | find_str = "processor"
54 | int_cpu = 0
55 | for line in get_cmd:
56 | if line.find(find_str) >= 0:
57 | int_cpu += 1
58 | return str(int_cpu) + "核"
59 |
60 | # 得到手机分辨率
61 | def get_app_pix(devices):
62 | result = os.popen("adb -s " + devices+ " shell wm size", "r")
63 | return result.readline().split("Physical size:")[1]
64 |
65 | if __name__=="__main__":
66 | getPhoneInfo("DU2TAN15AJ049163")
67 |
--------------------------------------------------------------------------------
/Base/BaseApk.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | __author__ = 'shikun'
4 | from math import floor
5 | import subprocess
6 | import os
7 | import json
8 | '''
9 | apk文件的读取信息
10 | '''
11 | class ApkInfo():
12 | def __init__(self, apkPath):
13 | self.apkPath = apkPath
14 |
15 | # 得到app的文件大小
16 | def getApkSize(self):
17 | size = floor(os.path.getsize(self.apkPath) / (1024 * 1000))
18 | return str(size) + "M"
19 |
20 |
21 | def getApkBaseInfo(self):
22 | p = subprocess.Popen("aapt dump badging %s" % self.apkPath, stdout=subprocess.PIPE,
23 | stderr=subprocess.PIPE,
24 | stdin=subprocess.PIPE, shell=True)
25 | (output, err) = p.communicate()
26 | match = re.compile("package: name='(\S+)' versionCode='(\d+)' versionName='(\S+)'").match(output.decode())
27 | if not match:
28 | raise Exception("can't get packageinfo")
29 | packagename = match.group(1)
30 | versionCode = match.group(2)
31 | versionName = match.group(3)
32 |
33 | print('packagename:' + packagename)
34 | print('versionCode:' + versionCode)
35 | print('versionName:' + versionName)
36 | return packagename, versionName, versionCode
37 |
38 | #得到应用名字
39 | def getApkName(self):
40 | p = subprocess.Popen("aapt dump badging %s" % self.apkPath, stdout=subprocess.PIPE,
41 | stderr=subprocess.PIPE,
42 | stdin=subprocess.PIPE, shell=True)
43 | (output, err) = p.communicate()
44 | t = output.decode().split()
45 | for item in t:
46 | # print(item)
47 | match = re.compile("application-label:(\S+)").search(item)
48 | if match is not None:
49 | return match.group(1)
50 |
51 |
52 | #得到启动类
53 |
54 | def getApkActivity(self):
55 | p = subprocess.Popen("aapt dump badging %s" % self.apkPath, stdout=subprocess.PIPE,
56 | stderr=subprocess.PIPE,
57 | stdin=subprocess.PIPE, shell=True)
58 | (output, err) = p.communicate()
59 | print("=====getApkActivity=========")
60 | match = re.compile("launchable-activity: name=(\S+)").search(output.decode())
61 | print("match=%s" %match)
62 | if match is not None:
63 | return match.group(1)
64 | if __name__ == '__main__':
65 | pass
66 | # ApkInfo(r"D:\app\appium\Img\Jianshu-2.3.1.apk").getApkActivity()
67 | # ApkInfo(r"D:\app\appium\Img\Jianshu-2.3.1.apk").getApkActivity()
68 | # # ApkInfo(r"D:\app\appium_study\Img\t.apk").get_apk_version()
69 | # # ApkInfo(r"D:\app\appium_study\Img\t.apk").get_apk_name()
70 | # ApkInfo(r"D:\app\appium_study\img\t.apk").get_apk_activity()
71 | # ApkInfo(r"D:\app\appium_study\Img\t.apk").get_apk_activity()
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Base/BaseAppiumServer.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import os
3 | import socket
4 | import urllib.request
5 | from urllib.error import URLError
6 | from multiprocessing import Process
7 | import time
8 | import platform
9 | import subprocess
10 |
11 | PATH = lambda p: os.path.abspath(
12 | os.path.join(os.path.dirname(__file__), p)
13 | )
14 | import threading
15 |
16 |
17 | class AppiumServer:
18 | def __init__(self, kwargs=None):
19 | self.kwargs = kwargs
20 |
21 | def start_server(self):
22 | """start the appium server
23 | """
24 | for i in range(0, len(self.kwargs)):
25 | cmd = "appium --session-override -p %s -bp %s -U %s" % (
26 | self.kwargs[i]["port"], self.kwargs[i]["bport"], self.kwargs[i]["devices"])
27 | print(cmd)
28 | if platform.system() == "Windows": # windows下启动server
29 | t1 = RunServer(cmd)
30 | p = Process(target=t1.start())
31 | p.start()
32 | while True:
33 | print("--------start_win_server-------------")
34 | if self.win_is_runnnig("http://127.0.0.1:" + self.kwargs[i]["port"] + "/wd/hub" + "/status"):
35 | print("-------win_server_ 成功--------------")
36 | break
37 | else:
38 | appium = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1,
39 | close_fds=True)
40 | while True:
41 | appium_line = appium.stdout.readline().strip().decode()
42 | time.sleep(1)
43 | print("---------start_server----------")
44 | if 'listener started' in appium_line or 'Error: listen' in appium_line:
45 | print("----server_ 成功---")
46 | break
47 |
48 | def win_is_runnnig(self, url):
49 | """Determine whether server is running
50 | :return:True or False
51 | """
52 | response = None
53 | time.sleep(1)
54 | try:
55 | response = urllib.request.urlopen(url, timeout=5)
56 |
57 | if str(response.getcode()).startswith("2"):
58 | return True
59 | else:
60 | return False
61 | except URLError:
62 | return False
63 | except socket.timeout:
64 | return False
65 | finally:
66 | if response:
67 | response.close()
68 |
69 | def stop_server(self, devices):
70 | sysstr = platform.system()
71 |
72 | if sysstr == 'Windows':
73 | os.popen("taskkill /f /im node.exe")
74 | else:
75 | for device in devices:
76 | # mac
77 | cmd = "lsof -i :{0}".format(device["port"])
78 | plist = os.popen(cmd).readlines()
79 | plisttmp = plist[1].split(" ")
80 | plists = plisttmp[1].split(" ")
81 | # print plists[0]
82 | os.popen("kill -9 {0}".format(plists[0]))
83 |
84 | def re_start_server(self):
85 | """reStart the appium server
86 | """
87 | # self.stop_server()
88 | # self.start_server()
89 | pass
90 |
91 |
92 | class RunServer(threading.Thread):
93 | def __init__(self, cmd):
94 | threading.Thread.__init__(self)
95 | self.cmd = cmd
96 |
97 | def run(self):
98 | os.system(self.cmd)
99 |
100 |
101 | if __name__ == "__main__":
102 |
103 | pass
--------------------------------------------------------------------------------
/Base/BaseConfig.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | # requests.packages.urllib.disable_warnings(InsecureRequestWarning)
4 | from urllib3 import disable_warnings
5 | from urllib3.connectionpool import InsecureRequestWarning
6 | disable_warnings(InsecureRequestWarning)
7 |
8 |
9 | import json
10 |
11 | # 封装HTTP GET请求方法
12 | def get(**kwargs):
13 | data = {}
14 | url = kwargs["protocol"] + "://"+kwargs["host"]+ kwargs["url"]
15 | print(url)
16 | r = requests.get(url, headers=kwargs.get("headers", None), verify=False)
17 | if r.status_code == 200 and len(r.text) > 0:
18 | r.encoding = 'UTF-8'
19 | data = json.loads(r.text)
20 | data["status_code"] = r.status_code
21 | # print(data)
22 | return data
23 | # 封装HTTP POST请求方法,支持上传图片
24 | def post(files=None, **kwargs):
25 | result = {}
26 | # url = kwargs["protocol"] + "://" + kwargs["host"] + ':' + str(kwargs["port"])+ kwargs["url"]
27 | url = kwargs["protocol"] + "://" + kwargs["host"] + kwargs["url"]
28 | data = None
29 | if kwargs.get("data", "none") != "none":
30 | data = json.dumps(kwargs["data"])
31 | print(data)
32 | print(url)
33 | r = requests.post(url, files=files, data=data, verify=False, headers=kwargs["headers"])
34 | result["status_code"] = r.status_code
35 | if r.status_code == 200 and len(r.text) > 0:
36 | r.encoding = 'UTF-8'
37 | result = json.loads(r.text)
38 | result["status_code"] = r.status_code
39 | return result
40 | '''
41 | 登陆
42 | '''
43 | def post_login(**kwargs):
44 | result = {}
45 | # url = kwargs["protocol"] + "://" + kwargs["host"] + ':' + str(kwargs["port"])+ kwargs["url"]
46 | url = kwargs["protocol"] + "://" + kwargs["host"] + kwargs["url"]
47 | data = None
48 | if kwargs.get("data", "none") != "none":
49 | data = json.dumps(kwargs["data"])
50 | print(data)
51 | print(url)
52 | r = requests.post(url, data=data, verify=False, headers=kwargs["headers"])
53 | if len(r.text):
54 | r.encoding = 'UTF-8'
55 | result = json.loads(r.text)
56 | result["status_code"] = r.status_code
57 | if result["status_code"] == 200:
58 | result["cookie"] = r.headers.get("Set-Cookie")
59 | # print("--登陆接口--")
60 | # print(result)
61 | return result
62 | if __name__ == '__main__':
63 |
64 |
65 | headers = {'content-type': 'application/json'}
66 | post(protocol="http", host="ivt3.hschefu.com", port=9199, url="/login", data={'password': '12345678','username': 'xiangjin'}, headers=headers)
--------------------------------------------------------------------------------
/Base/BaseElementEnmu.py:
--------------------------------------------------------------------------------
1 |
2 | class Element(object):
3 |
4 | # 常用操作关键字
5 | find_element_by_id = "id"
6 | find_elements_by_id = "ids"
7 | INDEX = "index"
8 | find_elements_by_xpath = "xpaths"
9 | find_element_by_xpath = "xpath"
10 | find_element_by_css_selector = "css"
11 | find_element_by_class_name = "class_name"
12 | CLICK = "click"
13 | TAP = "tap"
14 | ACCESSIBILITY = "accessibility"
15 | ADB_TAP = "adb_tap"
16 | SWIPE_DOWN = "swipe_down"
17 | SWIPE_UP = "swipe_up"
18 | SWIPE_LEFT = "swipe_left"
19 | SET_VALUE = "set_value"
20 | GET_VALUE = "get_value"
21 | WAIT_TIME = 20
22 | PRESS_KEY_CODE = "press_keycode"
23 |
24 | GET_CONTENT_DESC = "get_content_desc"
25 |
26 | # 错误日志
27 | TIME_OUT = "timeout"
28 | NO_SUCH = "noSuch"
29 | WEB_DROVER_EXCEPTION = "WebDriverException"
30 | INDEX_ERROR = "index_error"
31 | STALE_ELEMENT_REFERENCE_EXCEPTION = "StaleElementReferenceException"
32 | DEFAULT_ERROR = "default_error"
33 |
34 | # 检查点
35 | CONTRARY = "contrary" # 相反检查点,表示如果检查元素存在就说明失败,如删除后,此元素依然存在
36 | CONTRARY_GETVAL = "contrary_getval" # 检查点关键字contrary_getval: 相反值检查点,如果对比成功,说明失败
37 | DEFAULT_CHECK = "default_check" # 默认检查点,就是查找页面元素
38 | COMPARE = "compare" # 历史数据和实际数据对比
39 | TOAST = "toast"
40 |
41 |
42 | RE_CONNECT = 1 # 是否打开失败后再次运行一次用例
43 |
44 | INFO_FILE = "info.pickle"
45 | SUM_FILE = "sum.pickle"
46 | DEVICES_FILE = "devices.pickle"
47 | REPORT_FILE = "Report.xlsx"
48 |
--------------------------------------------------------------------------------
/Base/BaseEmail.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from email.header import Header
3 | from email.mime.text import MIMEText
4 | from email.utils import parseaddr, formataddr
5 | from email.mime.multipart import MIMEMultipart
6 | from email.mime.application import MIMEApplication
7 | import smtplib
8 | import os
9 | PATH = lambda p: os.path.abspath(
10 | os.path.join(os.path.dirname(__file__), p)
11 | )
12 | def _format_addr(s):
13 | name, addr = parseaddr(s)
14 | return formataddr((Header(name, 'utf-8').encode(), addr))
15 | def send_mail(**kwargs):
16 | '''
17 | :param f: 附件路径
18 | :param to_addr:发给的人 []
19 | :return:
20 | '''
21 | from_addr = kwargs["mail_user"]
22 | password = kwargs["mail_pass"]
23 | # to_addr = "ashikun@126.com"
24 | smtp_server = kwargs["mail_host"]
25 |
26 | msg = MIMEMultipart()
27 |
28 | # msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
29 | msg['From'] = _format_addr('来自<%s>接口测试' % from_addr)
30 | msg['To'] = _format_addr(' <%s>' % kwargs["to_addr"])
31 | msg['Subject'] = Header(kwargs["header_msg"], 'utf-8').encode()
32 | msg.attach(MIMEText(kwargs["attach"], 'plain', 'utf-8'))
33 |
34 | if kwargs.get("report", "0") != "0":
35 | part = MIMEApplication(open(kwargs["report"], 'rb').read())
36 | part.add_header('Content-Disposition', 'attachment', filename=('gb2312', '', kwargs["report_name"]))
37 | msg.attach(part)
38 |
39 | server = smtplib.SMTP_SSL(smtp_server, kwargs["port"])
40 | server.set_debuglevel(1)
41 | server.login(from_addr, password)
42 | server.sendmail(from_addr, kwargs["to_addr"], msg.as_string())
43 | server.quit()
44 | if __name__ == '__main__':
45 | to_addr = ["284772894@qq.com"]
46 | mail_host = "smtp.qq.com"
47 | mail_user = "284772894@qq.com"
48 | mail_pass = "oftllbhnknegbjhb"
49 | port = "465"
50 | header_msg = "接口测试"
51 | attach = "接口测试"
52 | report = PATH("../Runner/report.xlsx")
53 | send_mail(to_addr = to_addr, mail_host = mail_host, mail_user=mail_user, port=port, mail_pass=mail_pass, header_msg=header_msg, report=report, attach=attach, report_name="接口测试报告")
54 |
--------------------------------------------------------------------------------
/Base/BaseError.py:
--------------------------------------------------------------------------------
1 | from Base.BaseElementEnmu import Element
2 |
3 | """
4 | element_info: 元素
5 | info: 用例说明
6 | current: 当前值
7 | history: 历史值
8 | type: 错误类型
9 | """
10 |
11 |
12 | def get_error(kw):
13 | elements = {
14 | Element.TIME_OUT: lambda: "==%s请求超时==" % kw["element_info"],
15 | Element.NO_SUCH: lambda: "==%s不存在==" % kw["element_info"],
16 | Element.WEB_DROVER_EXCEPTION: lambda: "==%s的driver错误==" % kw["element_info"],
17 | Element.INDEX_ERROR: lambda: "==%s索引错误==" % kw["element_info"],
18 | Element.STALE_ELEMENT_REFERENCE_EXCEPTION: lambda: "==%s页面元素已经发生==" % kw["element_info"],
19 | Element.DEFAULT_ERROR: lambda: "==请检查%s==" % kw["element_info"],
20 | Element.CONTRARY: lambda: "==检查点_%s失败_%s依然在页面==" % (kw["info"], kw["element_info"]),
21 | Element.CONTRARY_GETVAL: lambda: "==检查点_对比数据失败,当前取到到数据为:%s,历史取到数据为:%s" % (kw["current"], kw["history"]),
22 | Element.DEFAULT_CHECK: lambda: "==检查点_%s失败,请检查_%s==" % (kw["info"], kw["element_info"]),
23 | Element.COMPARE: lambda: "==检查点_对比数据失败,当前取到到数据为:%s,历史取到数据为:%s" % (kw["current"], kw["history"]),
24 | Element.TOAST: lambda: "==检查点_%s_查找弹框失败==" % kw["element_info"]
25 | }
26 | return elements[kw["type"]]()
27 |
--------------------------------------------------------------------------------
/Base/BaseExcel.py:
--------------------------------------------------------------------------------
1 | __author__ = 'shikun'
2 | import xlsxwriter
3 | import os
4 |
5 | PATH = lambda p: os.path.abspath(
6 | os.path.join(os.path.dirname(__file__), p)
7 | )
8 |
9 |
10 | class OperateReport:
11 | def __init__(self, wd):
12 | self.wd = wd
13 |
14 | def init(self, worksheet, data, devices):
15 | # 设置列行的宽高
16 | worksheet.set_column("A:A", 15)
17 | worksheet.set_column("B:B", 20)
18 | worksheet.set_column("C:C", 20)
19 | worksheet.set_column("D:D", 20)
20 | worksheet.set_column("E:E", 20)
21 |
22 | worksheet.set_row(1, 30)
23 | worksheet.set_row(2, 30)
24 | worksheet.set_row(3, 30)
25 | worksheet.set_row(4, 30)
26 | worksheet.set_row(5, 30)
27 | worksheet.set_row(6, 30)
28 | worksheet.set_row(7, 30)
29 | worksheet.set_row(8, 30)
30 |
31 | define_format_H1 = get_format(self.wd, {'bold': True, 'font_size': 18})
32 | define_format_H2 = get_format(self.wd, {'bold': True, 'font_size': 14})
33 | define_format_H1.set_border(1)
34 |
35 | define_format_H2.set_border(1)
36 | define_format_H1.set_align("center")
37 | define_format_H2.set_align("center")
38 | define_format_H2.set_bg_color("blue")
39 | define_format_H2.set_color("#ffffff")
40 |
41 | worksheet.merge_range('A1:E1', '测试报告总概况', define_format_H1)
42 | worksheet.merge_range('A2:E2', 'WebLink知识测试概括', define_format_H2)
43 |
44 | _write_center(worksheet, "A3", 'versionCode', self.wd)
45 | _write_center(worksheet, "A4", 'versionName', self.wd)
46 | _write_center(worksheet, "A5", 'packingTime', self.wd)
47 | _write_center(worksheet, "A6", '测试日期', self.wd)
48 |
49 | _write_center(worksheet, "B3", data['versionCode'], self.wd)
50 | _write_center(worksheet, "B4", data['versionName'], self.wd)
51 | _write_center(worksheet, "B5", data['packingTime'], self.wd)
52 | _write_center(worksheet, "B6", data['testDate'], self.wd)
53 |
54 | _write_center(worksheet, "C3", "用例总数", self.wd)
55 | _write_center(worksheet, "C4", "通过总数", self.wd)
56 | _write_center(worksheet, "C5", "失败总数", self.wd)
57 | _write_center(worksheet, "C6", "测试耗时", self.wd)
58 |
59 | # data1 = {"test_sum": 100, "test_success": 80, "test_failed": 20, "test_date": "2018-10-10 12:10"}
60 | _write_center(worksheet, "D3", data['sum'], self.wd)
61 | _write_center(worksheet, "D4", data['pass'], self.wd)
62 | _write_center(worksheet, "D5", data['fail'], self.wd)
63 | _write_center(worksheet, "D6", data['testSumDate'], self.wd)
64 |
65 | _write_center(worksheet, "E3", "脚本语言", self.wd)
66 |
67 | worksheet.merge_range('E4:E6', 'appium1.7+python3', get_format_center(self.wd))
68 | _write_center(worksheet, "A8", '机型', self.wd)
69 | _write_center(worksheet, "B8", '通过', self.wd)
70 | _write_center(worksheet, "C8", '失败', self.wd)
71 |
72 | temp = 9
73 | for item in devices:
74 | _write_center(worksheet, "A%s" % temp, item["phone_name"], self.wd)
75 | _write_center(worksheet, "B%s" % temp, item["pass"], self.wd)
76 | _write_center(worksheet, "C%s" % temp, item["fail"], self.wd)
77 | temp = temp + 1
78 |
79 | pie(self.wd, worksheet)
80 |
81 | def detail(self, worksheet, info):
82 | # 设置列行的宽高
83 | worksheet.set_column("A:A", 30)
84 | worksheet.set_column("B:B", 20)
85 | worksheet.set_column("C:C", 20)
86 | worksheet.set_column("D:D", 20)
87 | worksheet.set_column("E:E", 20)
88 | worksheet.set_column("F:F", 20)
89 | worksheet.set_column("G:G", 20)
90 | worksheet.set_column("H:H", 20)
91 | worksheet.set_column("I:I", 20)
92 | worksheet.set_column("J:J", 20)
93 |
94 | worksheet.set_row(1, 30)
95 | worksheet.set_row(2, 30)
96 | worksheet.set_row(3, 30)
97 | worksheet.set_row(4, 30)
98 | worksheet.set_row(5, 30)
99 | worksheet.set_row(6, 30)
100 | worksheet.set_row(7, 30)
101 | worksheet.set_row(8, 30)
102 | worksheet.set_row(9, 30)
103 | worksheet.set_row(10, 30)
104 |
105 | worksheet.merge_range('A1:J1', '测试详情', get_format(self.wd, {'bold': True, 'font_size': 18, 'align': 'center',
106 | 'valign': 'vcenter', 'bg_color': 'blue',
107 | 'font_color': '#ffffff'}))
108 | _write_center(worksheet, "A2", '机型', self.wd)
109 | _write_center(worksheet, "B2", '用例ID', self.wd)
110 | _write_center(worksheet, "C2", '用例介绍', self.wd)
111 | _write_center(worksheet, "D2", '用例函数', self.wd)
112 | _write_center(worksheet, "E2", '前置条件', self.wd)
113 | _write_center(worksheet, "F2", '操作步骤 ', self.wd)
114 | _write_center(worksheet, "G2", '检查点 ', self.wd)
115 | _write_center(worksheet, "H2", '测试结果 ', self.wd)
116 | _write_center(worksheet, "I2", '备注 ', self.wd)
117 | _write_center(worksheet, "J2", '截图', self.wd)
118 |
119 | temp = 3
120 | for item in info:
121 | # print(item)
122 | _write_center(worksheet, "A" + str(temp), item["phoneName"], self.wd)
123 | _write_center(worksheet, "B" + str(temp), item["id"], self.wd)
124 | _write_center(worksheet, "C" + str(temp), item["title"], self.wd)
125 | _write_center(worksheet, "D" + str(temp), item["caseName"], self.wd)
126 | _write_center(worksheet, "E" + str(temp), item["info"], self.wd)
127 | _write_center(worksheet, "F" + str(temp), item["step"], self.wd)
128 | _write_center(worksheet, "G" + str(temp), item["checkStep"], self.wd)
129 | _write_center(worksheet, "H" + str(temp), item["result"], self.wd)
130 | _write_center(worksheet, "I" + str(temp), item.get("msg", ""), self.wd)
131 | if item.get("img", "false") == "false":
132 | _write_center(worksheet, "J" + str(temp), "", self.wd)
133 | worksheet.set_row(temp, 30)
134 | else:
135 | worksheet.insert_image('J' + str(temp), item["img"],
136 | {'x_scale': 0.1, 'y_scale': 0.1, 'border': 1})
137 | worksheet.set_row(temp - 1, 110)
138 | temp = temp + 1
139 |
140 | def close(self):
141 | self.wd.close()
142 |
143 |
144 | def get_format(wd, option={}):
145 | return wd.add_format(option)
146 |
147 |
148 | # def link_format(wd):
149 | # red_format = wd.add_format({
150 | # 'font_color': 'red',
151 | # 'bold': 1,
152 | # 'underline': 1,
153 | # 'font_size': 12,
154 | # })
155 | def get_format_center(wd, num=1):
156 | return wd.add_format({'align': 'center', 'valign': 'vcenter', 'border': num})
157 |
158 |
159 | def set_border_(wd, num=1):
160 | return wd.add_format({}).set_border(num)
161 |
162 |
163 | def _write_center(worksheet, cl, data, wd):
164 | return worksheet.write(cl, data, get_format_center(wd))
165 |
166 |
167 | def set_row(worksheet, num, height):
168 | worksheet.set_row(num, height)
169 |
170 | # 生成饼形图
171 |
172 |
173 | def pie(workbook, worksheet):
174 | chart1 = workbook.add_chart({'type': 'pie'})
175 | chart1.add_series({
176 | 'name': '自动化测试统计',
177 | 'categories': '=测试总况!$C$4:$C$5',
178 | 'values': '=测试总况!$D$4:$D$5',
179 | })
180 | chart1.set_title({'name': '测试统计'})
181 | chart1.set_style(10)
182 | worksheet.insert_chart('A9', chart1, {'x_offset': 25, 'y_offset': 10})
183 |
184 |
185 | if __name__ == '__main__':
186 | sum = {'testSumDate': '25秒', 'sum': 10, 'pass': 5, 'testDate': '2017-06-05 15:26:49', 'fail': 5,
187 | 'appVersion': '17051515', 'appSize': '14M', 'appName': "'简书'"}
188 | info = [{"id": 1, "title": "第一次打开", "caseName": "testf01", "result": "通过", "phoneName": "三星"},
189 | {"id": 1, "title": "第一次打开",
190 | "caseName": "testf01", "result": "通过", "img": "d:\\1.PNG", "phoneName": "华为"}]
191 | workbook = xlsxwriter.Workbook('Report.xlsx')
192 | worksheet = workbook.add_worksheet("测试总况")
193 | worksheet2 = workbook.add_worksheet("测试详情")
194 | bc = OperateReport(wd=workbook)
195 | bc.init(worksheet, sum)
196 | bc.detail(worksheet2, info)
197 | bc.close()
198 | #
199 |
--------------------------------------------------------------------------------
/Base/BaseFile.py:
--------------------------------------------------------------------------------
1 | __author__ = 'shikun'
2 |
3 | import os
4 |
5 |
6 | '''
7 | 操作文件
8 | '''
9 |
10 |
11 | def write_data(f, method='w+', data=""):
12 | if not os.path.isfile(f):
13 | print('文件不存在,写入数据失败')
14 | else:
15 | with open(f, method, encoding="utf-8") as fs:
16 | fs.write(data + "\n")
17 |
18 |
19 | def mkdir_file(f, method='w+'):
20 | if not os.path.isfile(f):
21 | with open(f, method, encoding="utf-8") as fs:
22 | print("创建文件%s成功" % f)
23 | pass
24 | else:
25 | print("%s文件已经存在,创建失败" % f)
26 | pass
27 |
28 |
29 | def remove_file(f):
30 | if os.path.isfile(f):
31 | os.remove(f)
32 | else:
33 | print("%s文件不存在,无法删除" % f)
34 |
--------------------------------------------------------------------------------
/Base/BaseInit.py:
--------------------------------------------------------------------------------
1 | from Base.BaseElementEnmu import Element
2 | from Base.BasePickle import *
3 | from Base.BaseFile import *
4 |
5 | PATH = lambda p: os.path.abspath(
6 | os.path.join(os.path.dirname(__file__), p)
7 | )
8 |
9 |
10 | def mk_file():
11 | destroy()
12 | mkdir_file(PATH("../Log/"+Element.INFO_FILE))
13 | mkdir_file(PATH("../Log/"+Element.SUM_FILE))
14 | mkdir_file(PATH("../Log/" + Element.DEVICES_FILE))
15 |
16 | data = read(PATH("../Log/"+Element.INFO_FILE))
17 | # data["appName"] = apkInfo.getApkName()
18 | # data["appSize"] = apkInfo.getApkSize()
19 | # data["appVersion"] = apkInfo.getApkBaseInfo()[2]
20 | data["versionCode"] = "40"
21 | data["versionName"] = "1.4.0"
22 | data["packingTime"] = "2017/12/4 13:00"
23 | data["sum"] = 0
24 | data["pass"] = 0
25 | data["fail"] = 0
26 | write(data=data, path=PATH("../Log/"+Element.SUM_FILE))
27 |
28 |
29 | def init(devices):
30 | # 每次都重新安装uiautomator2都两个应用
31 | pass
32 | # os.popen("adb -s %s uninstall io.appium.uiautomator2.server.test" % devices)
33 | # os.popen("adb -s %s uninstall io.appium.uiautomator2.server" % devices)
34 | # os.popen("adb -s %s install -r %s" % (devices, PATH("../app/appium-uiautomator2-server-v0.1.9.apk")))
35 | # os.popen("adb -s %s install -r %s" % (devices, PATH("../app/appium-uiautomator2-server-debug-androidTest.apk")))
36 | # os.popen("adb install -r "+PATH("../app/android-system-webview-60.apk"))
37 |
38 |
39 | def destroy():
40 | remove_file(PATH("../Log/"+Element.INFO_FILE))
41 | remove_file(PATH("../Log/"+Element.SUM_FILE))
42 | remove_file(PATH("../Log/"+Element.DEVICES_FILE))
43 |
44 |
45 | if __name__ == '__main__':
46 | print(destroy())
47 | # print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
48 |
--------------------------------------------------------------------------------
/Base/BaseIosCommand.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 |
3 | import os
4 |
5 | '''
6 | 获取ios下的硬件信息
7 | '''
8 |
9 |
10 | def get_ios_devices():
11 | devices = []
12 | result = subprocess.Popen("ideviceinfo -k UniqueDeviceID", shell=True, stdout=subprocess.PIPE,
13 | stderr=subprocess.PIPE).stdout.readlines()
14 |
15 | for item in result:
16 | t = item.decode().split("\n")
17 | if len(t) >= 2:
18 | devices.append(t[0])
19 | print(devices)
20 | return devices
21 |
22 |
23 | def get_ios_version(udid):
24 | command = "ideviceinfo -u %s -k ProductVersion" % udid
25 | result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
26 | stderr=subprocess.PIPE).stdout.readlines()
27 | for item in result:
28 | t = item.decode().split("\n")
29 | if len(t) >= 2:
30 | return t[0]
31 |
32 |
33 | def get_ios_product_name(udid):
34 | command = "ideviceinfo -u %s -k DeviceName" % udid
35 | result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
36 | stderr=subprocess.PIPE).stdout.readlines()
37 | for item in result:
38 | t = item.decode().split("\n")
39 | if len(t) >= 2:
40 | return t[0]
41 |
42 |
43 | # 编译facebook的wda到真机
44 | def build_wda_ios(udid):
45 | os.popen(
46 | "xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination id=" + udid + " test")
47 |
48 |
49 | if __name__ == '__main__':
50 | udid = get_ios_devices()[0]
51 | print(get_ios_product_name(udid))
52 |
--------------------------------------------------------------------------------
/Base/BaseLog.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import time
3 | import os
4 | from time import sleep
5 | import threading
6 | from Base.BaseAndroidPhone import getPhoneInfo
7 |
8 | PATH = lambda p: os.path.abspath(
9 | os.path.join(os.path.dirname(__file__), p)
10 | )
11 |
12 |
13 | class Log:
14 | def __init__(self, devices):
15 | get_phone = getPhoneInfo(devices)
16 | phone_name = get_phone["brand"] + "_" + get_phone["model"] + "_" + "android" + "_" + get_phone["release"]
17 | global logger, resultPath, logPath
18 | resultPath = PATH("../log")
19 | logPath = os.path.join(resultPath, (phone_name + time.strftime('%Y%m%d%H%M%S', time.localtime())))
20 | if not os.path.exists(logPath):
21 | os.makedirs(logPath)
22 | self.checkNo = 0
23 | self.logger = logging.getLogger()
24 | self.logger.setLevel(logging.INFO)
25 |
26 | # create handler,write log
27 | fh = logging.FileHandler(os.path.join(logPath, "outPut.log"))
28 | # Define the output format of formatter handler
29 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
30 | fh.setFormatter(formatter)
31 |
32 | self.logger.addHandler(fh)
33 |
34 | def getMyLogger(self):
35 | """get the logger
36 | :return:logger
37 | """
38 | return self.logger
39 |
40 | def buildStartLine(self, caseNo):
41 | """build the start log
42 | :param caseNo:
43 | :return:
44 | """
45 | startLine = "---- " + caseNo + " " + " " + \
46 | " ----"
47 | # startLine = "---- " + caseNo + " " + "START" + " " + \
48 | # " ----"
49 | self.logger.info(startLine)
50 |
51 | def buildEndLine(self, caseNo):
52 | """build the end log
53 | :param caseNo:
54 | :return:
55 | """
56 | endLine = "---- " + caseNo + " " + "END" + " " + \
57 | " ----"
58 | self.logger.info(endLine)
59 | self.checkNo = 0
60 |
61 | def writeResult(self, result):
62 | """write the case result(OK or NG)
63 | :param result:
64 | :return:
65 | """
66 | reportPath = os.path.join(logPath, "report.txt")
67 | flogging = open(reportPath, "a")
68 | try:
69 | flogging.write(result + "\n")
70 | finally:
71 | flogging.close()
72 | pass
73 |
74 | def resultOK(self, caseNo):
75 | self.writeResult(caseNo + ": OK")
76 |
77 | def resultNG(self, caseNo, reason):
78 | self.writeResult(caseNo + ": NG--" + reason)
79 |
80 | def checkPointOK(self, driver, caseName, checkPoint):
81 | """write the case's checkPoint(OK)
82 | :param driver:
83 | :param caseName:
84 | :param checkPoint:
85 | :return:
86 | """
87 | self.checkNo += 1
88 |
89 | self.logger.info("[CheckPoint_" + str(self.checkNo) + "]: " + checkPoint + ": OK")
90 | print("==用例_%s检查点成功==" % caseName)
91 | # take shot 默认去掉成功截图
92 | # self.screenshotOK(driver, caseName)
93 |
94 | def checkPointNG(self, driver, caseName, checkPoint):
95 | """write the case's checkPoint(NG)
96 | :param driver:
97 | :param caseName:
98 | :param checkPoint:
99 | :return:
100 | """
101 | self.checkNo += 1
102 |
103 | self.logger.info("[CheckPoint_" + str(self.checkNo) + "]: " + checkPoint + ": NG")
104 |
105 | # take shot
106 | return self.screenshotNG(driver, caseName)
107 |
108 | def screenshotOK(self, driver, caseName):
109 | """screen shot
110 | :param driver:
111 | :param caseName:
112 | :return:
113 | """
114 | screenshotPath = os.path.join(logPath, caseName)
115 | screenshotName = "CheckPoint_" + str(self.checkNo) + "_OK.png"
116 |
117 | # wait for animations to complete before taking screenshot
118 | sleep(1)
119 | # driver.get_screenshot_as_file(os.path.join(screenshotPath, screenshotName))
120 | driver.get_screenshot_as_file(os.path.join(screenshotPath + screenshotName))
121 |
122 | def screenshotNG(self, driver, caseName):
123 | """screen shot
124 | :param driver:
125 | :param caseName:
126 | :return:
127 | """
128 | screenshotPath = os.path.join(logPath, caseName)
129 | screenshotName = "CheckPoint_" + str(self.checkNo) + "_NG.png"
130 |
131 | # wait for animations to complete before taking screenshot
132 | sleep(1)
133 | driver.get_screenshot_as_file(os.path.join(screenshotPath + screenshotName))
134 | return os.path.join(screenshotPath + screenshotName)
135 |
136 | def screenshotERROR(self, driver, caseName):
137 | """screen shot
138 | :param driver:
139 | :param caseName:
140 | :return:
141 | """
142 | screenshotPath = os.path.join(logPath, caseName)
143 | screenshotName = "ERROR.png"
144 |
145 | # wait for animations to complete before taking screenshot
146 | sleep(1)
147 | driver.get_screenshot_as_file(os.path.join(screenshotPath, screenshotName))
148 |
149 |
150 | class myLog:
151 | """
152 | This class is used to get log
153 | """
154 |
155 | log = None
156 | mutex = threading.Lock()
157 |
158 | def __init__(self):
159 | pass
160 |
161 | @staticmethod
162 | def getLog(devices):
163 | if myLog.log is None:
164 | myLog.mutex.acquire()
165 | myLog.log = Log(devices)
166 | myLog.mutex.release()
167 |
168 | return myLog.log
169 |
170 |
171 | if __name__ == "__main__":
172 | logTest = myLog.getLog("devices")
173 | # logger = logTest.getMyLogger()
174 | logTest.buildStartLine("11111111111111111111111")
--------------------------------------------------------------------------------
/Base/BaseOperate.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import os
4 | import threading
5 |
6 | import appium.common.exceptions
7 | from selenium.webdriver.common.by import By
8 | from selenium.webdriver.support import expected_conditions
9 |
10 | __author__ = 'shikun'
11 | # -*- coding: utf-8 -*-
12 | from selenium.webdriver.support.ui import WebDriverWait
13 | import selenium.common.exceptions
14 | from Base.BaseElementEnmu import Element as be
15 | import time
16 | import os
17 |
18 | '''
19 | # 此脚本主要用于查找元素是否存在,操作页面元素
20 | '''
21 |
22 |
23 | class OperateElement:
24 | def __init__(self, driver=""):
25 | self.driver = driver
26 |
27 | def findElement(self, mOperate):
28 | '''
29 | 查找元素.mOperate,dict|list
30 | operate_type:对应的操作
31 | element_info:元素详情
32 | find_type: find类型
33 | '''
34 | try:
35 | if type(mOperate) == list: # 多检查点
36 | for item in mOperate:
37 | if item.get("is_webview", "0") == 1: # 1表示切换到webview
38 | self.switchToWebview()
39 | elif item.get("is_webview", "0") == 2:
40 | self.switchToNative()
41 | # if item.get("element_info", "0") == "0": # 如果没有页面元素,就不检测是页面元素,可能是滑动等操作
42 | # return {"result": True}
43 | t = item["check_time"] if item.get("check_time", "0") != "0" else be.WAIT_TIME
44 | WebDriverWait(self.driver, t).until(lambda x: self.elements_by(item))
45 | return {"result": True}
46 | if type(mOperate) == dict: # 单检查点
47 | if mOperate.get("is_webview", "0") == 1 and self.switchToWebview() is False: # 1表示切换到webview
48 | print("切换到webview失败,请确定是否在webview页面")
49 | return {"result": False, "webview": False}
50 | elif mOperate.get("is_webview", "0") == 2:
51 | self.switchToNative()
52 | if mOperate.get("element_info", "0") == "0": # 如果没有页面元素,就不检测是页面元素,可能是滑动等操作
53 | return {"result": True}
54 | t = mOperate["check_time"] if mOperate.get("check_time",
55 | "0") != "0" else be.WAIT_TIME # 如果自定义检测时间为空,就用默认的检测等待时间
56 | WebDriverWait(self.driver, t).until(lambda x: self.elements_by(mOperate)) # 操作元素是否存在
57 | return {"result": True}
58 | except selenium.common.exceptions.TimeoutException:
59 | # print("==查找元素超时==")
60 | return {"result": False, "type": be.TIME_OUT}
61 | except selenium.common.exceptions.NoSuchElementException:
62 | # print("==查找元素不存在==")
63 | return {"result": False, "type": be.NO_SUCH}
64 | except selenium.common.exceptions.WebDriverException:
65 | # print("WebDriver出现问题了")
66 | return {"result": False, "type": be.WEB_DROVER_EXCEPTION}
67 |
68 | '''
69 | 查找元素.mOperate是字典
70 | operate_type:对应的操作
71 | element_info:元素详情
72 | find_type: find类型
73 | testInfo: 用例介绍
74 | logTest: 记录日志
75 | device: 设备名
76 | '''
77 |
78 | def operate(self, mOperate, testInfo, logTest, device):
79 | res = self.findElement(mOperate)
80 | if res["result"]:
81 | return self.operate_by(mOperate, testInfo, logTest, device)
82 | else:
83 | return res
84 |
85 | def operate_by(self, operate, testInfo, logTest, device):
86 | try:
87 | info = operate.get("element_info", " ") + "_" + operate.get("operate_type", " ") + str(operate.get(
88 | "code", " ")) + operate.get("msg", " ")
89 | logTest.buildStartLine(testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + info) # 记录日志
90 | print("==操作步骤:%s==" % info)
91 |
92 | if operate.get("operate_type", "0") == "0": # 如果没有此字段,说明没有相应操作,一般是检查点,直接判定为成功
93 | return {"result": True}
94 |
95 | # threading._start_new_thread(self.click_windows(device),())
96 | elements = {
97 | be.SWIPE_DOWN: lambda: self.swipeToDown(),
98 | be.SWIPE_UP: lambda: self.swipeToUp(),
99 | be.CLICK: lambda: self.click(operate),
100 | be.GET_VALUE: lambda: self.get_value(operate),
101 | be.SET_VALUE: lambda: self.set_value(operate),
102 | be.ADB_TAP: lambda: self.adb_tap(operate, device),
103 | be.TAP: lambda: self.tap(operate),
104 | be.GET_CONTENT_DESC: lambda: self.get_content_desc(operate),
105 | be.PRESS_KEY_CODE: lambda: self.press_keycode(operate)
106 |
107 | }
108 | return elements[operate.get("operate_type")]()
109 | except IndexError:
110 | logTest.buildStartLine(
111 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate["element_info"] + "索引错误") # 记录日志
112 | # print(operate["element_info"] + "索引错误")
113 | return {"result": False, "type": be.INDEX_ERROR}
114 |
115 | except selenium.common.exceptions.NoSuchElementException:
116 | logTest.buildStartLine(
117 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate[
118 | "element_info"] + "页面元素不存在或没加载完成") # 记录日志
119 | # print(operate["element_info"] + "页面元素不存在或没有加载完成")
120 | return {"result": False, "type": be.NO_SUCH}
121 | except selenium.common.exceptions.StaleElementReferenceException:
122 | logTest.buildStartLine(
123 | testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + operate[
124 | "element_info"] + "页面元素已经变化") # 记录日志
125 | # print(operate["element_info"] + "页面元素已经变化")
126 | return {"result": False, "type": be.STALE_ELEMENT_REFERENCE_EXCEPTION}
127 | except KeyError:
128 | # 如果key不存在,一般都是在自定义的page页面去处理了,这里直接返回为真
129 | return {"result": True}
130 |
131 | # 获取到元素到坐标点击,主要解决浮动层遮档无法触发driver.click的问题
132 | def adb_tap(self, mOperate, device):
133 |
134 | bounds = self.elements_by(mOperate).location
135 | x = str(bounds["x"])
136 | y = str(bounds["y"])
137 |
138 | cmd = "adb -s " + device + " shell input tap " + x + " " + y
139 | print(cmd)
140 | os.system(cmd)
141 |
142 | return {"result": True}
143 |
144 | def tap(self, operate):
145 | x1 = operate["bounds"][0][0]
146 | y1 = operate["bounds"][0][1]
147 |
148 | x2 = operate["bounds"][0][1]
149 | y2 = operate["bounds"][1][1]
150 | self.driver.tap([(x1, y1), (x2, y2)], operate.get("duration", 300))
151 |
152 | return {"result": True}
153 |
154 | def toast(self, xpath, logTest, testInfo):
155 | logTest.buildStartLine(testInfo[0]["id"] + "_" + testInfo[0]["title"] + "_" + "查找弹窗元素_" + xpath) # 记录日志
156 | try:
157 | WebDriverWait(self.driver, 10, 0.5).until(
158 | expected_conditions.presence_of_element_located((By.XPATH, xpath)))
159 | return {"result": True}
160 | except selenium.common.exceptions.TimeoutException:
161 | return {"result": False}
162 | except selenium.common.exceptions.NoSuchElementException:
163 | return {"result": False}
164 |
165 | # 点击事件
166 | def click(self, mOperate):
167 | # print(self.driver.page_source)
168 | if mOperate["find_type"] == be.find_element_by_id or mOperate["find_type"] == be.find_element_by_xpath:
169 | self.elements_by(mOperate).click()
170 | elif mOperate.get("find_type") == be.find_elements_by_id:
171 | self.elements_by(mOperate)[mOperate["index"]].click()
172 | return {"result": True}
173 |
174 | # code 事件
175 | def press_keycode(self, mOperate):
176 | self.driver.press_keycode(mOperate.get("code", 0))
177 | return {"result": True}
178 |
179 | def get_content_desc(self, mOperate):
180 | result = self.elements_by(mOperate).get_attribute("contentDescription")
181 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result)
182 | return {"result": True, "text": "".join(re_reulst)}
183 |
184 | '''
185 | 切换native
186 |
187 | '''
188 |
189 | def switchToNative(self):
190 | self.driver.switch_to.context("NATIVE_APP") # 切换到native
191 |
192 | '''
193 | 切换webview
194 | '''
195 |
196 | def switchToWebview(self):
197 | try:
198 | n = 1
199 | while n < 10:
200 | time.sleep(3)
201 | n = n + 1
202 | print(self.driver.contexts)
203 | for cons in self.driver.contexts:
204 | if cons.lower().startswith("webview"):
205 | self.driver.switch_to.context(cons)
206 | # print(self.driver.page_source)
207 | self.driver.execute_script('document.querySelectorAll("html")[0].style.display="block"')
208 | self.driver.execute_script('document.querySelectorAll("head")[0].style.display="block"')
209 | self.driver.execute_script('document.querySelectorAll("title")[0].style.display="block"')
210 | print("切换webview成功")
211 | return {"result": True}
212 | return {"result": False}
213 | except appium.common.exceptions.NoSuchContextException:
214 | print("切换webview失败")
215 | return {"result": False, "text": "appium.common.exceptions.NoSuchContextException异常"}
216 |
217 | # 左滑动
218 | def swipeLeft(self, mOperate):
219 | width = self.driver.get_window_size()["width"]
220 | height = self.driver.get_window_size()["height"]
221 | x1 = int(width * 0.75)
222 | y1 = int(height * 0.5)
223 | x2 = int(width * 0.05)
224 | self.driver(x1, y1, x2, y1, 600)
225 |
226 | # swipe start_x: 200, start_y: 200, end_x: 200, end_y: 400, duration: 2000 从200滑动到400
227 | def swipeToDown(self):
228 | height = self.driver.get_window_size()["height"]
229 | x1 = int(self.driver.get_window_size()["width"] * 0.5)
230 | y1 = int(height * 0.25)
231 | y2 = int(height * 0.75)
232 |
233 | self.driver.swipe(x1, y1, x1, y2, 1000)
234 | # self.driver.swipe(0, 1327, 500, 900, 1000)
235 | print("--swipeToDown--")
236 | return {"result": True}
237 |
238 | def swipeToUp(self):
239 | height = self.driver.get_window_size()["height"]
240 | width = self.driver.get_window_size()["width"]
241 | self.driver.swipe(width / 2, height * 3 / 4, width / 2, height / 4)
242 | print("执行上拉")
243 | return {"result": True}
244 | # for i in range(n):
245 | # self.driver.swipe(540, 800, 540, 560, 0)
246 | # time.sleep(2)
247 |
248 | def swipeToRight(self):
249 | height = self.driver.get_window_size()["height"]
250 | width = self.driver.get_window_size()["width"]
251 | x1 = int(width * 0.05)
252 | y1 = int(height * 0.5)
253 | x2 = int(width * 0.75)
254 | self.driver.swipe(x1, y1, x1, x2, 1000)
255 | # self.driver.swipe(0, 1327, 500, 900, 1000)
256 | print("--swipeToUp--")
257 |
258 | def set_value(self, mOperate):
259 | """
260 | 输入值,代替过时的send_keys
261 | :param mOperate:
262 | :return:
263 | """
264 | self.elements_by(mOperate).send_keys(mOperate["msg"])
265 | return {"result": True}
266 |
267 | def get_value(self, mOperate):
268 | '''
269 | 读取element的值,支持webview下获取值
270 | :param mOperate:
271 | :return:
272 | '''
273 |
274 | if mOperate.get("find_type") == be.find_elements_by_id:
275 | element_info = self.elements_by(mOperate)[mOperate["index"]]
276 | if mOperate.get("is_webview", "0") == 1:
277 | result = element_info.text
278 | else:
279 | result = element_info.get_attribute("text")
280 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result) # 只匹配中文,大小写,字母
281 | return {"result": True, "text": "".join(re_reulst)}
282 |
283 | element_info = self.elements_by(mOperate)
284 | if mOperate.get("is_webview", "0") == 1:
285 | result = element_info.text
286 | else:
287 | result = element_info.get_attribute("text")
288 |
289 | re_reulst = re.findall(r'[a-zA-Z\d+\u4e00-\u9fa5]', result)
290 | return {"result": True, "text": "".join(re_reulst)}
291 |
292 | def click_windows(self, device):
293 | try:
294 | button0 = 'com.huawei.systemmanager:id/btn_allow'
295 | # button1 = 'com.android.packageinstaller:id/btn_allow_once'
296 | # button2 = 'com.android.packageinstaller:id/bottom_button_two'
297 | # button3 = 'com.android.packageinstaller:id/btn_continue_install'
298 | # button4 = 'android:id/button1'
299 | # button5 = 'vivo:id/vivo_adb_install_ok_button'
300 | button_list = [button0]
301 | for elem in button_list:
302 | find = self.driver.find_element_by_id(elem)
303 | WebDriverWait(self.driver, 1).until(lambda x: self.elements_by(find(elem)))
304 | bounds = find.location
305 | x = str(bounds["x"])
306 | y = str(bounds["y"])
307 | cmd = "adb -s " + device + " shell input tap " + x + " " + y
308 | print(cmd)
309 | os.system(cmd)
310 | print("==点击授权弹框_%s==" % elem)
311 | except selenium.common.exceptions.TimeoutException:
312 | # print("==查找元素超时==")
313 | pass
314 | except selenium.common.exceptions.NoSuchElementException:
315 | # print("==查找元素不存在==")
316 | pass
317 | except selenium.common.exceptions.WebDriverException:
318 | # print("WebDriver出现问题了")
319 | pass
320 |
321 | # 封装常用的标签
322 | def elements_by(self, mOperate):
323 |
324 | elements = {
325 | be.find_element_by_id: lambda: self.driver.find_element_by_id(mOperate["element_info"]),
326 | be.find_element_by_xpath: lambda: self.driver.find_element_by_xpath(mOperate["element_info"]),
327 | be.find_element_by_css_selector: lambda: self.driver.find_element_by_css_selector(mOperate['element_info']),
328 | be.find_element_by_class_name: lambda: self.driver.find_element_by_class_name(mOperate['element_info']),
329 | be.find_elements_by_id: lambda: self.driver.find_elements_by_id(mOperate['element_info'])
330 |
331 | }
332 | return elements[mOperate["find_type"]]()
333 |
--------------------------------------------------------------------------------
/Base/BasePickle.py:
--------------------------------------------------------------------------------
1 | __author__ = "shikun"
2 | import pickle
3 | import os
4 |
5 | def write(data, path="data.pickle"):
6 | with open(path, 'wb') as f:
7 | pickle.dump(data, f, 0)
8 | def read(path):
9 | with open(path, 'rb') as f:
10 | try:
11 | data = pickle.load(f)
12 | except EOFError:
13 | data = {}
14 | # print("读取文件错误")
15 | # print("------read-------")
16 | # print(data)
17 | return data
18 |
19 | def readInfo(path):
20 | # data = []
21 | with open(path, 'rb') as f:
22 | try:
23 | data = pickle.load(f)
24 | print(data)
25 | except EOFError:
26 | data = []
27 | # print("读取文件错误")
28 | # print("------read-------")
29 | # print(data)
30 | return data
31 |
32 |
33 |
34 | def writeInfo(data="", path="data.pickle"):
35 | """
36 |
37 | :type data: dict
38 | """
39 | _read = readInfo(path)
40 | result = []
41 | if _read:
42 | _read.append(data)
43 | result = _read
44 | else:
45 | result.append(data)
46 | with open(path, 'wb') as f:
47 | # print("------writeInfo-------")
48 | # print(result)
49 | pickle.dump(result, f)
50 |
51 | if __name__ == "__main__":
52 | # write("用例失败重连过一次,失败原因:", "../Log/connect64dd15b8-ca91-11e7-87ae-38c98647adce.pickle")
53 | pass
--------------------------------------------------------------------------------
/Base/BaseRunner.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from Base.BaseAppiumServer import AppiumServer
3 | from Base.BaseLog import myLog
4 | import unittest
5 | from appium import webdriver
6 | import os
7 | from Base.BaseElementEnmu import Element
8 | import platform
9 | import time
10 | from Base.BaseYaml import getYam
11 |
12 | PATH = lambda p: os.path.abspath(
13 | os.path.join(os.path.dirname(__file__), p)
14 | )
15 |
16 |
17 | def appium_testcase(devices):
18 | desired_caps = {}
19 |
20 | if str(devices["platformName"]).lower() == "android":
21 | # desired_caps['appPackage'] = devices["appPackage"]
22 | # desired_caps['appActivity'] = devices["appActivity"]
23 | desired_caps['udid'] = devices["deviceName"]
24 | desired_caps['app'] = devices["app"]
25 | # desired_caps["recreateChromeDriverSessions"] = "True"
26 | # 解决多次切换到webview报错问题,每次切换到非chrome-Driver时kill掉session 注意这个设置在appium 1.5版本上才做了处理
27 | else:
28 | # desired_caps['automationName'] = devices["automationName"] # Xcode8.2以上无UIAutomation,需使用XCUITest
29 | desired_caps['bundleId'] = devices["bundleId"]
30 | desired_caps['udid'] = devices["udid"]
31 | # desired_caps['newCommandTimeout'] = 3600 # 1 hour
32 |
33 | desired_caps['platformVersion'] = devices["platformVersion"]
34 | desired_caps['platformName'] = devices["platformName"]
35 | desired_caps["automationName"] = devices['automationName']
36 | desired_caps['deviceName'] = devices["deviceName"]
37 | desired_caps["noReset"] = "True"
38 | desired_caps['noSign'] = "True"
39 | desired_caps["unicodeKeyboard"] = "True"
40 | desired_caps["resetKeyboard"] = "True"
41 | desired_caps["systemPort"] = devices["systemPort"]
42 |
43 | # desired_caps['app'] = devices["app"]
44 | remote = "http://127.0.0.1:" + str(devices["port"]) + "/wd/hub"
45 | # remote = "http://127.0.0.1:" + "4723" + "/wd/hub"
46 | driver = webdriver.Remote(remote, desired_caps)
47 | return driver
48 |
49 |
50 | class ParametrizedTestCase(unittest.TestCase):
51 | """ TestCase classes that want to be parametrized should
52 | inherit from this class.
53 | """
54 |
55 | def __init__(self, methodName='runTest', param=None):
56 | super(ParametrizedTestCase, self).__init__(methodName)
57 | global devicess
58 | devicess = param
59 |
60 | @classmethod
61 | def setUpClass(cls):
62 | pass
63 | cls.driver = appium_testcase(devicess)
64 | cls.devicesName = devicess["deviceName"]
65 | cls.logTest = myLog().getLog(cls.devicesName) # 每个设备实例化一个日志记录器
66 |
67 | def setUp(self):
68 | pass
69 |
70 | @classmethod
71 | def tearDownClass(cls):
72 | cls.driver.close_app()
73 | cls.driver.quit()
74 | pass
75 | def tearDown(self):
76 | pass
77 |
78 | @staticmethod
79 | def parametrize(testcase_klass, param=None):
80 | # print("---parametrize-----")
81 | # print(param)
82 | testloader = unittest.TestLoader()
83 | testnames = testloader.getTestCaseNames(testcase_klass)
84 | suite = unittest.TestSuite()
85 | for name in testnames:
86 | suite.addTest(testcase_klass(name, param=param))
87 | return suite
88 |
--------------------------------------------------------------------------------
/Base/BaseStatistics.py:
--------------------------------------------------------------------------------
1 | import xlsxwriter
2 |
3 | from Base.BaseAndroidPhone import getPhoneInfo
4 | from Base.BaseElementEnmu import Element
5 | from Base.BaseExcel import OperateReport
6 | from Base.BaseInit import destroy
7 | from Base.BasePickle import *
8 | from datetime import datetime
9 |
10 | PATH = lambda p: os.path.abspath(
11 | os.path.join(os.path.dirname(__file__), p)
12 | )
13 |
14 | '''
15 | 统计数据相关
16 | '''
17 |
18 | '''
19 | result bool
20 | logTest 记录日志类 class
21 | driver
22 | testinfo
23 |
24 | '''
25 |
26 |
27 | def countInfo(**kwargs):
28 | # get_phone = getPhoneInfo(kwargs["devices"])
29 | # phone_name = get_phone["brand"] + "_" + get_phone["model"] + "_" + "android" + "_" + get_phone["release"]
30 | _info = {}
31 | step = "" # 操作步骤信息
32 | check_step = "" # 检查点步骤信息
33 |
34 | for case in kwargs["testCase"]:
35 | step = step + case["info"] + "\n"
36 |
37 | if type(kwargs["testCheck"]) == list: # 检查点为列表
38 | for check in kwargs["testCheck"]:
39 | check_step = check_step + check["info"] + "\n"
40 | elif type(kwargs["testCheck"]) == dict:
41 | check_step = kwargs["testCheck"]["info"]
42 | else:
43 | print("获取检查点步骤数据错误,请检查")
44 | print(kwargs["testCheck"])
45 |
46 | _info["step"] = step # 用例操作步骤
47 | _info["checkStep"] = check_step # 用例检查点
48 |
49 | if kwargs["result"]:
50 | _info["result"] = "通过"
51 | kwargs["logTest"].checkPointOK(driver=kwargs["driver"], caseName=kwargs["testInfo"][0]["title"],
52 | checkPoint=kwargs["caseName"] + "_" + kwargs["testInfo"][0].get(
53 | "msg", " "))
54 | else:
55 | _info["result"] = "失败" # 用例接开关
56 | _info["img"] = kwargs["logTest"].checkPointNG(driver=kwargs["driver"], caseName=kwargs["testInfo"][0]["title"],
57 | checkPoint=kwargs["caseName"] + "_" + kwargs["testInfo"][0].get(
58 | "msg", " "))
59 | _info["id"] = kwargs["testInfo"][0]["id"] # 用例id
60 | _info["title"] = kwargs["testInfo"][0]["title"] # 用例名称
61 | _info["caseName"] = kwargs["caseName"] # 测试函数
62 | _info["phoneName"] = kwargs["phoneName"] # 手机名
63 | _info["msg"] = kwargs["testInfo"][0].get("msg", "") # 备注
64 | _info["info"] = kwargs["testInfo"][0]["info"] # 前置条件
65 |
66 | writeInfo(data=_info, path=PATH("../Log/" + Element.INFO_FILE))
67 | # print(read(PATH("../Log/info.pickle")))
68 |
69 |
70 | # 本地没有设备用例的记录统计
71 | def countSumNoDevices(devices, result, _read, phone_name):
72 |
73 | if _read is None:
74 | _read = []
75 | # get_phone = getPhoneInfo(devices)
76 | # phone_name = get_phone["brand"] + "_" + get_phone["model"] + "_" + "android" + "_" + get_phone["release"]
77 | app = {"phone_name": phone_name, "pass": 0, "fail": 0, "device": devices}
78 | if result:
79 | app["pass"] = 1
80 | else:
81 | app["fail"] = 1
82 | _read.append(app)
83 | write(data=_read, path=PATH("../Log/" + Element.DEVICES_FILE))
84 | print(read(PATH("../Log/" + Element.DEVICES_FILE)))
85 |
86 | return
87 |
88 |
89 | # 统计各个设备成功失败的用例数
90 | def countSumDevices(devices, result, phone_name):
91 | _read = readInfo(PATH("../Log/" + Element.DEVICES_FILE))
92 | if _read:
93 | for item in _read:
94 | if item["device"] == devices: # 本地已经存在该设备记录
95 | if result:
96 | item["pass"] = item["pass"] + 1
97 | else:
98 | item["fail"] = item["fail"] + 1
99 | write(data=_read, path=PATH("../Log/" + Element.DEVICES_FILE))
100 | return
101 | countSumNoDevices(devices, result, _read, phone_name=phone_name)
102 | print(read(PATH("../Log/" + Element.DEVICES_FILE)))
103 |
104 | # else:
105 | # print("------0------")
106 | # countSumNoDevices(devices, result)
107 | # print("---countSumDevices---")
108 | # print(read(PATH("../Log/" + Element.DEVICES_FILE)))
109 |
110 |
111 | # 统计所有用例数
112 | def countSum(result):
113 | # print("----countSum----")
114 | data = {"sum": 0, "pass": 0, "fail": 0}
115 | _read = read(PATH("../Log/sum.pickle"))
116 | if _read:
117 | data = _read
118 | data["sum"] = data["sum"] + 1
119 | if result:
120 | data["pass"] = data["pass"] + 1
121 | else:
122 | data["fail"] = data["fail"] + 1
123 | write(data=data, path=PATH("../Log/" + Element.SUM_FILE))
124 | # print(read(PATH("../Log/sum.pickle")))
125 |
126 |
127 | # def write_reconnect(msg, path=""):
128 | # write(msg, path=path)
129 | # # print(read_reconnect(path))
130 |
131 |
132 | def countDate(testDate, testSumDate):
133 | print("--------- countDate------")
134 | data = read(PATH("../Log/" + Element.SUM_FILE))
135 | print(data)
136 | if data:
137 | data["testDate"] = testDate
138 | data["testSumDate"] = testSumDate
139 | write(data=data, path=PATH("../Log/" + Element.SUM_FILE))
140 | else:
141 | print("统计数据失败")
142 |
143 |
144 | '''
145 | 测试报告
146 | '''
147 |
148 |
149 | def writeExcel():
150 | workbook = xlsxwriter.Workbook(PATH('../Report/' + Element.REPORT_FILE))
151 | worksheet = workbook.add_worksheet("测试总况")
152 | worksheet2 = workbook.add_worksheet("测试详情")
153 | operateReport = OperateReport(workbook)
154 | operateReport.init(worksheet, read(PATH("../Log/" + Element.SUM_FILE)),
155 | read(PATH("../Log/" + Element.DEVICES_FILE)))
156 | operateReport.detail(worksheet2, readInfo(PATH("../Log/" + Element.INFO_FILE)))
157 | operateReport.close()
158 |
159 | # destroy() # 删除文件
160 |
161 |
162 | if __name__ == '__main__':
163 | # data = {'result': '失败', 'caseName': 'FirstOpenTest', 'title': '第一次打开', 'phoneName': 'samsung_GT-I9500_android_4.4.2', 'img': 'D:\\app\\appium\\log\\samsung_GT-I9500_android_4.4.220170607184558\\第一次打开CheckPoint_1_NG.png', 'id': 'test001'}
164 | # writeInfo(data, PATH("../Log/info.pickle"))
165 | # writeInfo(data, PATH("../Log/info.pickle"))
166 | # _read = readInfo(PATH("../Log/info.pickle"))
167 | writeExcel()
168 |
--------------------------------------------------------------------------------
/Base/BaseWebServer.py:
--------------------------------------------------------------------------------
1 | from http.server import BaseHTTPRequestHandler,HTTPServer
2 | from multiprocessing import Process
3 | import subprocess
4 |
5 | pid = 0
6 | class myHandler(BaseHTTPRequestHandler):
7 | def do_GET(self):
8 | d_result = {}
9 | find_devices="devices"
10 | req = self.path.split('&')
11 | if req[0].find(find_devices) > 0:
12 | d_result["devices"] = req[0].split("=")[1]
13 | d_result["log"] = req[1].split("=")[1]
14 | basePickle.write_pickle(d_result, gv.CRASH_LOG_PATH)
15 | self.send_response(200)
16 | self.send_header('Content-type','text/html')
17 | self.end_headers()
18 | # Send the html message
19 | self.wfile.write(b"Hello World !") #发送信息给客户端
20 | print("do_GET")
21 | def open_web_server():
22 | server = HTTPServer((gv.HOST, gv.PORT), myHandler)
23 | print('Started httpserver on port ', gv.PORT)
24 | server.serve_forever()
25 | if __name__=="__main__":
26 | p = Process(target=open_web_server, args=())
27 | p.start()
28 | # subprocess.Popen("taskkill /F /T /PID " + str(p.pid), shell=True)
29 | print("kkk")
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Base/BaseYaml.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import yaml
3 | from yaml.scanner import ScannerError
4 | import os
5 |
6 |
7 | def getYam(path):
8 | try:
9 | with open(path, encoding='utf-8') as f:
10 | x = yaml.load(f)
11 | return [True, x]
12 | except FileNotFoundError:
13 | print("==用例文件不存在==")
14 | app = {'check': [{'element_info': '', 'operate_type': 'get_value', 'find_type': 'ids', 'info': '用例文件不存在'}],
15 | 'testinfo': [{'title': '', 'id': '', 'info': '', "msg": ""}],
16 | 'testcase': [{'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''},
17 | {'element_info': '', 'msg': "", 'operate_type': '', 'find_type': '', 'info': ''},
18 | {'element_info': '', 'msg': '', 'operate_type': '', 'find_type': '', 'info': ''},
19 | {'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}]}
20 |
21 | return [False, app]
22 | except yaml.scanner.ScannerError:
23 | app = {'check': [{'element_info': '', 'operate_type': 'get_value', 'find_type': 'ids', 'info': '用例文件格式错误'}],
24 | 'testinfo': [{'title': '', 'id': '', 'info': '', "msg": " "}],
25 | 'testcase': [{'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''},
26 | {'element_info': '', 'msg': "", 'operate_type': '', 'find_type': '', 'info': ''},
27 | {'element_info': '', 'msg': '', 'operate_type': '', 'find_type': '', 'info': ''},
28 | {'element_info': '', 'info': '', 'operate_type': '', 'find_type': ''}]}
29 | print("==用例格式错误==")
30 | return [False, app]
31 |
32 |
33 | if __name__ == '__main__':
34 | import os
35 |
36 | PATH = lambda p: os.path.abspath(
37 | os.path.join(os.path.dirname(__file__), p)
38 | )
39 | t = getYam(PATH("../yaml/test.yaml"))
40 | print(t)
41 |
42 | # port = str(random.randint(4700, 4900))
43 | # bpport = str(random.randint(4700, 4900))
44 | # devices = "DU2TAN15AJ049163"
45 | #
46 | # cmd1 = "appium --session-override -p %s -bp %s -U %s" % (port, bpport, devices)
47 | # print(cmd1)
48 | # os.popen(cmd1)
49 |
--------------------------------------------------------------------------------
/Base/HTMLTestRunner.py:
--------------------------------------------------------------------------------
1 | """
2 | A TestRunner for use with the Python unit testing framework. It
3 | generates a HTML report to show the result at a glance.
4 |
5 | The simplest way to use this is to invoke its main method. E.g.
6 |
7 | import unittest
8 | import HTMLTestRunner
9 |
10 | ... define your tests ...
11 |
12 | if __name__ == '__main__':
13 | HTMLTestRunner.main()
14 |
15 |
16 | For more customization options, instantiates a HTMLTestRunner object.
17 | HTMLTestRunner is a counterpart to unittest's TextTestRunner. E.g.
18 |
19 | # output to a file
20 | fp = file('my_report.html', 'wb')
21 | runner = HTMLTestRunner.HTMLTestRunner(
22 | stream=fp,
23 | title='My unit test',
24 | description='This demonstrates the report output by HTMLTestRunner.'
25 | )
26 |
27 | # Use an external stylesheet.
28 | # See the Template_mixin class for more customizable options
29 | runner.STYLESHEET_TMPL = ''
30 |
31 | # run the test
32 | runner.run(my_test_suite)
33 |
34 |
35 | ------------------------------------------------------------------------
36 | Copyright (c) 2004-2007, Wai Yip Tung
37 | All rights reserved.
38 |
39 | Redistribution and use in source and binary forms, with or without
40 | modification, are permitted provided that the following conditions are
41 | met:
42 |
43 | * Redistributions of source code must retain the above copyright notice,
44 | this list of conditions and the following disclaimer.
45 | * Redistributions in binary form must reproduce the above copyright
46 | notice, this list of conditions and the following disclaimer in the
47 | documentation and/or other materials provided with the distribution.
48 | * Neither the name Wai Yip Tung nor the names of its contributors may be
49 | used to endorse or promote products derived from this software without
50 | specific prior written permission.
51 |
52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
53 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
55 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
56 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
57 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
58 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
59 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63 | """
64 |
65 | # URL: http://tungwaiyip.info/software/HTMLTestRunner.html
66 |
67 | __author__ = "Wai Yip Tung"
68 | __version__ = "0.8.2"
69 |
70 |
71 | """
72 | Change History
73 |
74 | Version 0.8.2
75 | * Show output inline instead of popup window (Viorel Lupu).
76 |
77 | Version in 0.8.1
78 | * Validated XHTML (Wolfgang Borgert).
79 | * Added description of test classes and test cases.
80 |
81 | Version in 0.8.0
82 | * Define Template_mixin class for customization.
83 | * Workaround a IE 6 bug that it does not treat
290 |
291 | %(heading)s
292 | %(report)s
293 | %(ending)s
294 |
295 |