Skip to content

Commit 4168606

Browse files
author
Álvaro Santos Laserna López
committed
add more methods
1 parent a28e731 commit 4168606

5 files changed

Lines changed: 207 additions & 43 deletions

File tree

testui/elements/testui_element.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ def testui_error(driver, exception):
4242
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
4343
Path(root_dir + "/report_screenshots").mkdir(parents=True, exist_ok=True)
4444
image_name = f'ERROR-{driver.device_name}-{current_time}.png'
45-
driver.save_screenshot(image_name, directory='report_screenshots/')
46-
exception = exception + f'{logger.bcolors.FAIL} \n Screenshot taken and saved as: ' \
47-
f'report_screenshots/testui-{image_name}{logger.bcolors.ENDC}\n'
45+
try:
46+
driver.save_screenshot(image_name, directory='report_screenshots/')
47+
exception += f'{logger.bcolors.FAIL} \n Screenshot taken and saved as: ' \
48+
f'report_screenshots/testui-{image_name}{logger.bcolors.ENDC}\n'
49+
except Exception as error:
50+
exception += f'{logger.bcolors.FAIL} \nCould not take screenshot:{logger.bcolors.ENDC}\n {error}'
4851
full_exception = error_with_traceback(exception)
4952
if driver.soft_assert:
5053
logger.log_error(full_exception)
@@ -440,9 +443,10 @@ def screenshot(self, image_name='cropped_image.png'):
440443

441444
return self
442445

443-
def find_image_match(self, image_name, threshold=0.9, image_match='', max_scale=2.0):
446+
def find_image_match(self, image_name, threshold=0.9, image_match='', max_scale=2.0, min_scale=0.3):
444447
"""
445448
Takes screenshot of the element and compares it with the one you provide as 'image_name'
449+
:param min_scale:
446450
:param max_scale:
447451
:param image_match: returns the image with a rectangle showing the match
448452
:param image_name: relative path to image
@@ -453,7 +457,7 @@ def find_image_match(self, image_name, threshold=0.9, image_match='', max_scale=
453457
self.__is_not = False
454458
self.screenshot(self.device_name + '.png')
455459
found, p = ImageRecognition(self.device_name + '.png', image_name, threshold, self.device_name) \
456-
.compare(image_match, max_scale)
460+
.compare(image_match=image_match, max_scale=max_scale, min_scale=min_scale)
457461
if not found and not is_not:
458462
self.__show_error(f'{self.device_name}: The images compared did not match. threshold={threshold}. '
459463
f'Matched = {p}')
@@ -466,6 +470,30 @@ def find_image_match(self, image_name, threshold=0.9, image_match='', max_scale=
466470

467471
return self
468472

473+
def is_image_match(self, image_name, threshold=0.9, image_match='', max_scale=2.0, min_scale=0.3):
474+
"""
475+
Takes screenshot of the element and compares it with the one you provide as 'image_name'
476+
:param min_scale:
477+
:param max_scale:
478+
:param image_match: returns the image with a rectangle showing the match
479+
:param image_name: relative path to image
480+
:param threshold: limit to consider image as a match (0 to 1)
481+
:return: Elements
482+
"""
483+
is_not = self.__is_not
484+
self.__is_not = False
485+
self.screenshot(self.device_name + '.png')
486+
found, p = ImageRecognition(self.device_name + '.png', image_name, threshold, self.device_name) \
487+
.compare(image_match=image_match, max_scale=max_scale, min_scale=min_scale)
488+
if not found and not is_not:
489+
return False
490+
if found and is_not:
491+
return False
492+
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + '/'
493+
os.remove(root_dir + self.device_name + '.png')
494+
495+
return True
496+
469497
def swipe(self, start_x=None, start_y=None, end_x=None, end_y=None, el=None, duration=None):
470498
"""
471499
It swipes from element to the el(second element) or to the coordinates you define with start_x, start_y,
@@ -686,7 +714,11 @@ def press_and_compare(self, image, milliseconds=1000, threshold=0.9, fps_reducti
686714
return self
687715

688716
def collection_size(self):
717+
self.__is_collection = False
718+
index = self.index
719+
self.index = 0
689720
self.wait_until_visible()
721+
self.index = index
690722
self.__is_collection = True
691723
return len(self.__find_by_collection())
692724

@@ -743,4 +775,8 @@ def class_name(cls, text: str):
743775

744776
@classmethod
745777
def parent(cls, parent_method, child_method):
746-
return f'fromParent({child_method}).{parent_method}'
778+
return f'fromParent({parent_method}).{child_method}'
779+
780+
@classmethod
781+
def child(cls, parent_method, child_method):
782+
return f'childSelector({child_method}).{parent_method}'

testui/support/appium_driver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def set_extra_caps(self, caps=None):
7777
caps = {}
7878
for cap in caps:
7979
self.__desired_capabilities[cap] = caps[cap]
80+
return self
8081

8182
def set_app_path(self, path: str):
8283
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

testui/support/logger.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ def log_warn(message, jump_line=False):
7373

7474

7575
def log_test_name(message, jump_line=False):
76+
log_info(message, jump_line)
77+
new_line = ''
78+
if jump_line:
79+
new_line = '\n'
80+
file = open(__file_tests(), 'a+')
81+
file.write(f'{message}\n{new_line}')
82+
file.close()
83+
84+
85+
def log_info(message, jump_line=False):
7686
now = datetime.now()
7787
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
7888
new_line = ''
@@ -93,3 +103,9 @@ def __file_log(log_file='stdout.log'):
93103
else:
94104
file_name = f'appium_logs/{log_file}'
95105
return root_dir + file_name
106+
107+
108+
def __file_tests(log_file='report_cases.txt'):
109+
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + '/'
110+
Path(root_dir + "appium_logs").mkdir(parents=True, exist_ok=True)
111+
return root_dir + log_file

testui/support/parallel.py

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import os
2-
import multiprocessing as mp
31
import argparse
2+
import multiprocessing as mp
3+
import os
44
import time
5+
import subprocess
56

67
from testui.support import logger
78

@@ -13,10 +14,21 @@ def parallel_testui():
1314
os.remove('report_fails.txt')
1415
except FileNotFoundError:
1516
pass
17+
try:
18+
os.remove('report_cases.txt')
19+
except FileNotFoundError:
20+
pass
21+
test_run_id = None
22+
if args.testrail_id is not None:
23+
test_run_id = args.testrail_id
24+
logger.log_info(f'Starting testrail run {test_run_id}')
25+
elif args.testrail is not None:
26+
test_run_id = __start_run_id(args, args.testrail)
27+
logger.log_info(f'Created testrail run {test_run_id} with testrail name: {args.testrail}')
1628
number_of_cases = get_total_number_of_cases(args)
17-
logger.log_test_name(f'------------ Total Number of Test Cases: {number_of_cases} ---------------')
29+
logger.log_info(f'------------ Total Number of Test Cases: {number_of_cases} ---------------')
1830
start = time.time()
19-
__start_processes(args.markers, args)
31+
__start_processes(args.markers, args, test_run_id)
2032
end_time = time.time() - start
2133
f = open('fails.txt')
2234
fails = f.read()
@@ -88,8 +100,9 @@ def get_total_number_of_cases(args):
88100
number = cases.split(' selected')[0]
89101
number_of_cases += int(number)
90102
if args.single_thread_marker is not None:
91-
output = subprocess.run(['pytest', '-m', f'{args.single_thread_marker} {args.general_markers}', f'--collect-only'],
92-
stdout=subprocess.PIPE)
103+
output = subprocess.run(
104+
['pytest', '-m', f'{args.single_thread_marker} {args.general_markers}', f'--collect-only'],
105+
stdout=subprocess.PIPE)
93106
response = output.stdout
94107
string_response = response.__str__()
95108
if string_response.__contains__(' / '):
@@ -122,10 +135,22 @@ def __arg_parser():
122135
parser.add_argument('--parallel', type=int,
123136
help='number of parallel threads')
124137

125-
parser.add_argument('--s', type=__str2bool, nargs='?',
138+
parser.add_argument('--s', type=__str2bool, nargs='?',
126139
const=True, default=False,
127140
help='make more verbose logs (pytest -s)')
128141

142+
parser.add_argument('--testrail', type=str,
143+
help='Use test rail and specify name. '
144+
'You will have to add a file testrail.cfg in root project dir')
145+
146+
parser.add_argument('--testrail_id', type=str,
147+
help='Use test rail and specify testrail id. '
148+
'You will have to add a file testrail.cfg in root project dir')
149+
150+
parser.add_argument('--testrail_pwd', type=str,
151+
help='Use test rail and specify password. '
152+
'You will have to add a file testrail.cfg in root project dir')
153+
129154
parser.add_argument('--general', type=str,
130155
help='general flags to put in all threads')
131156

@@ -159,13 +184,13 @@ def __arg_parser():
159184
else:
160185
logger.log(f'General flags: {args.general}')
161186
if args.single_thread_marker is not None:
162-
logger.log_test_name(f'Single Thread marker: {args.single_thread_marker} {args.general_markers}')
187+
logger.log_info(f'Single Thread marker: {args.single_thread_marker} {args.general_markers}')
163188
return args
164189

165190

166191
def __str2bool(v):
167192
if isinstance(v, bool):
168-
return v
193+
return v
169194
if v.lower() in ('yes', 'true', 't', 'y', '1'):
170195
return True
171196
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
@@ -174,7 +199,7 @@ def __str2bool(v):
174199
raise argparse.ArgumentTypeError('Boolean value expected.')
175200

176201

177-
def __start_processes(markers: list, args):
202+
def __start_processes(markers: list, args, test_run_id=None):
178203
ps = list()
179204
amount = len(markers) // args.parallel
180205
amount_plus = 0
@@ -193,18 +218,62 @@ def __start_processes(markers: list, args):
193218
if len(markers) > i:
194219
tags.append(markers[i])
195220
i += 1
196-
logger.log_test_name(f'Thread {j} has markers: {tags}')
197-
ps.append(mp.Process(target=__process, args=(tags, args, j)))
221+
logger.log_info(f'Thread {j} has markers: {tags}')
222+
ps.append(mp.Process(target=__process, args=(tags, args, j, test_run_id)))
198223
for p in ps:
199224
p.daemon = True
200225
p.start()
201226
for p in ps:
202227
p.join()
203228
if args.single_thread_marker is not None:
204-
__process([args.single_thread_marker], args)
229+
__process([args.single_thread_marker], args, 0, test_run_id)
205230

206231

207-
def __process(markers: list, args, thread=0):
232+
def __start_run_id(args, test_run_name):
233+
end_marker = ''
234+
for i, marker in enumerate(args.markers):
235+
if i == len(args.markers):
236+
end_marker += f'{marker} {args.general_markers}'
237+
else:
238+
end_marker += f'{marker} {args.general_markers} or '
239+
if args.single_thread_marker is not None:
240+
end_marker += f'{args.single_thread_marker} {args.general_markers}'
241+
with open('testrail_id_file.txt', 'wb') as out:
242+
cmd = ['pytest', '-m', end_marker, '--testrail', f'--tr-config=testrail.cfg',
243+
f'--tr-testrun-name={test_run_name}']
244+
if args.testrail_pwd is not None:
245+
cmd.append(f'--tr-password={args.testrail_pwd}')
246+
process = subprocess.Popen(cmd, stdout=out, stderr=out)
247+
i = 0
248+
while True:
249+
time.sleep(0.1)
250+
out = open('testrail_id_file.txt')
251+
text = out.read()
252+
if text.__contains__("New testrun created") or i > 50:
253+
out.close()
254+
process.terminate()
255+
process.wait()
256+
os.remove('testrail_id_file.txt')
257+
if text.__contains__('ID='):
258+
id_test = text.split("ID=")[1]
259+
if text.split("ID=")[1].__contains__('\n'):
260+
id_test = text.split("ID=")[1].split("\n")[0]
261+
logger.log_info(f'Test run: {id_test}')
262+
return id_test
263+
raise Exception('Failed to create Test Run')
264+
elif text.__contains__("Failed to create testrun"):
265+
out.close()
266+
process.send_signal(signal=2)
267+
process.terminate()
268+
process.wait()
269+
os.remove('testrail_id_file.txt')
270+
logger.log_error(f'Failed to create Test Run: \n {text}')
271+
raise Exception(f'Failed to create Test Run')
272+
out.close()
273+
i += 1
274+
275+
276+
def __process(markers: list, args, thread=0, test_run_id=None):
208277
for i, marker in enumerate(markers):
209278
try:
210279
os.remove(f'.my_cache_dir_{thread}/v/cache/lastfailed')
@@ -215,10 +284,15 @@ def __process(markers: list, args, thread=0):
215284
quiet = '-q'
216285
if args.s:
217286
quiet = '-s'
287+
testrail = ''
288+
if test_run_id is not None:
289+
testrail = f'--testrail --tr-config=testrail.cfg --tr-run-id={test_run_id}'
290+
if args.testrail_pwd is not None:
291+
testrail += f' --tr-password={args.testrail_pwd}'
218292
cache = f'-o cache_dir=.my_cache_dir_{thread}'
219293
start_1 = time.time()
220-
logger.log(f'Starting: pytest {quiet} -m "{marker} {args.general_markers}" {args.general} {cache}')
221-
pr_1 = os.system(f'pytest {quiet} -m "{marker} {args.general_markers}" {args.general} {cache}')
294+
logger.log(f'Starting: pytest {quiet} -m "{marker} {args.general_markers}" {testrail} {args.general} {cache}')
295+
pr_1 = os.system(f'pytest {quiet} -m "{marker} {args.general_markers}" {testrail} {args.general} {cache}')
222296
file = open('fails.txt', 'a+')
223297
file.write(f'{pr_1}')
224298
file.close()

0 commit comments

Comments
 (0)