From 36fdfd18f1f7faf69aae2adfa005ab533c2452cc Mon Sep 17 00:00:00 2001 From: xingwenyang Date: Tue, 20 Jun 2023 16:56:08 +0800 Subject: [PATCH 1/2] add web mock --- flybirds/core/dsl/globalization/i18n.py | 4 +- flybirds/core/dsl/step/request.py | 6 ++ flybirds/core/plugin/event/scenario.py | 2 + .../plugins/default/web/interception.py | 53 +++++++++++++- .../core/plugin/plugins/default/web/page.py | 73 +++++++++++++++++++ .../core/plugin/plugins/default/web/step.py | 19 ++++- 6 files changed, 151 insertions(+), 6 deletions(-) diff --git a/flybirds/core/dsl/globalization/i18n.py b/flybirds/core/dsl/globalization/i18n.py index 1427fb81..c4af7c0e 100644 --- a/flybirds/core/dsl/globalization/i18n.py +++ b/flybirds/core/dsl/globalization/i18n.py @@ -202,6 +202,8 @@ "compare target element[{target_ele}] with compared text path of [{compared_text_path}]": [ "对比文本元素[{target_ele}]和基准文本路径[{compared_text_path}]"], "call external party api of method[{method}] and url[{url}] and data[{data}] and headers[{headers}]": [ - "调外部接口并传参请求方式[{method}]与请求链接[{url}]与请求内容[{data}]与请求标头[{headers}]"] + "调外部接口并传参请求方式[{method}]与请求链接[{url}]与请求内容[{data}]与请求标头[{headers}]"], + "open service [{service}] bind mockCase[{mock_case_id}]": [ + "开启服务[{service}]绑定MockCase[{mock_case_id}]"], }, } diff --git a/flybirds/core/dsl/step/request.py b/flybirds/core/dsl/step/request.py index 01884d94..185d6e54 100644 --- a/flybirds/core/dsl/step/request.py +++ b/flybirds/core/dsl/step/request.py @@ -191,3 +191,9 @@ def request_compare_value(context, service=None, target_json_path=None, """ g_Context.step.request_compare_value(context, service, target_json_path, expect_value) + + +@step("open service [{service}] bind mockCase[{mock_case_id}]") +@ele_wrap +def open_request_mock(context, service, mock_case_id): + g_Context.step.open_web_mock(context, service, mock_case_id) diff --git a/flybirds/core/plugin/event/scenario.py b/flybirds/core/plugin/event/scenario.py index 703e19b6..e0d5d181 100644 --- a/flybirds/core/plugin/event/scenario.py +++ b/flybirds/core/plugin/event/scenario.py @@ -111,6 +111,7 @@ def scenario_fail(context, scenario): try: GlobalContext.step.clear_all_request_body(context) GlobalContext.step.clear_all_request_mock(context) + GlobalContext.step.remove_web_mock(context) except: log.info("failed to remove mock and cache") @@ -149,6 +150,7 @@ def scenario_success(context, scenario): try: GlobalContext.step.clear_all_request_body(context) GlobalContext.step.clear_all_request_mock(context) + GlobalContext.step.remove_web_mock(context) except: log.info("failed to remove mock and cache") launch_helper.app_start("scenario_success_page") diff --git a/flybirds/core/plugin/plugins/default/web/interception.py b/flybirds/core/plugin/plugins/default/web/interception.py index 8490af55..206256e7 100644 --- a/flybirds/core/plugin/plugins/default/web/interception.py +++ b/flybirds/core/plugin/plugins/default/web/interception.py @@ -95,6 +95,53 @@ def add_some_interception_mock(service_str, mock_case_id_str): gr.set_value('interceptionValues', interception_values) + @staticmethod + def open_web_mock(service_str, mock_case_id_str, request_mock_key_value: list): + if service_str is None or mock_case_id_str is None: + log.error('[addSomeInterceptionMock] param can not be none. ') + return + + service_list = service_str.strip().split(',') + mock_case_id_list = mock_case_id_str.strip().split(',') + if len(service_list) != len(mock_case_id_list): + message = f"serviceCount[{service_str}] not equal " \ + f"mockCaseCount[{mock_case_id_str}]" + raise FlybirdsException(message) + + interception_values = request_mock_key_value + for i, service in enumerate(service_list): + if service is not None and len(service.strip()) > 0: + if ":" in service: + split_service = service.split(":") + if split_service[0].strip() == "reg": + interception_values.append({ + "max": 1, + "key": split_service[1].strip(), + "value": mock_case_id_list[i].strip(), + "method": "reg" + }) + elif split_service[0].strip() == "equ": + interception_values.append({ + "max": 1, + "key": split_service[1].strip(), + "value": mock_case_id_list[i].strip(), + "method": "equ" + }) + else: + interception_values.append({ + "max": 1, + "key": service.strip(), + "value": mock_case_id_list[i].strip(), + "method": "contains" + }) + else: + interception_values.append({ + "max": 1, + "key": service.strip(), + "value": mock_case_id_list[i].strip(), + "method": "contains" + }) + @staticmethod def remove_some_interception_mock(service_str): service_list = service_str.strip().split(',') @@ -352,7 +399,7 @@ def request_compare_value(operation, target_path, expect_value): raise FlybirdsException(message) @staticmethod - def compare_images(context,target_picture_path, compared_picture_path, threshold=None): + def compare_images(context, target_picture_path, compared_picture_path, threshold=None): if threshold is None: threshold = 0.95 @@ -426,7 +473,7 @@ def compare_images(context,target_picture_path, compared_picture_path, threshold x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(image2, (x, y), (x + w, y + h), (0, 0, 255), 2) - cv2.imencode('.png',image2)[1].tofile(diff_file_path) + cv2.imencode('.png', image2)[1].tofile(diff_file_path) message = f'Diff percentage of image [{threshold}] ' \ f'has been saved in path [{diff_file_path}]' @@ -464,7 +511,7 @@ def compare_dom_element_text(target_text, compared_text_path): # Compare the text content of the two DOM elements if text1 == text2: same = True - message = f'The text of the two UI elements are the same'\ + message = f'The text of the two UI elements are the same' \ f' [{text1}]:' \ f' [{compared_text_path}] - [{text2}]:' log.info(message) diff --git a/flybirds/core/plugin/plugins/default/web/page.py b/flybirds/core/plugin/plugins/default/web/page.py index 904b6427..2ef2dbf0 100644 --- a/flybirds/core/plugin/plugins/default/web/page.py +++ b/flybirds/core/plugin/plugins/default/web/page.py @@ -19,9 +19,14 @@ from flybirds.utils.dsl_helper import is_number from flybirds.utils import file_helper from flybirds.core.exceptions import FlybirdsException +import urllib.parse +import threading +from urllib.parse import urlsplit, urlunsplit __open__ = ["Page"] +mock_lock: threading.Lock = threading.Lock() + class Page: """Web Page Class""" @@ -346,6 +351,48 @@ def handle_request(request): gr.set_value("interceptionRequest", interception_request) +def mock_rules(url: str, request_mock_key_value: list): + if url is None or len(url.strip()) <= 0: + return None + scheme, netloc, path, query, fragment = urlsplit(url) + temp_path = urlunsplit(("", "", path, query, fragment)) + if temp_path is None or len(temp_path.strip()) <= 0: + return None + if path is None or len(path.strip()) <= 0: + return None + match_mock_key = None + with mock_lock: + for mock_rule in request_mock_key_value: + mock_find = False + if mock_rule is not None and mock_rule.get("key") and mock_rule.get("value") and mock_rule.get("max"): + if mock_rule.get("key") is not None and len(mock_rule.get("key").strip()) > 0 and len( + mock_rule.get("value").strip()) > 0: + if mock_rule.get("max") <= 0: + continue + method = mock_rule.get("method", None) + if method is None or method == "contains": + if mock_rule.get("key").strip() in temp_path: + mock_find = True + elif method == "equ": + if mock_rule.get("key").strip().strip("/").strip("\\") == path.lower().strip().strip("/").strip( + "\\"): + mock_find = True + elif method == "reg": + match = re.search(mock_rule.get("key").strip(), temp_path) + if match: + mock_find = True + else: + if mock_rule.get("key").strip() in temp_path: + mock_find = True + if mock_find: + match_mock_key = mock_rule + mock_rule["max"] = mock_rule.get("max") - 1 + log.info( + f"{path} match mock case: key f{mock_rule.get('key')}----value {mock_rule.get('value')}") + break + return match_mock_key + + def handle_route(route): abort_domain_list = gr.get_web_info_value("abort_domain_list", []) parsed_uri = urlparse(route.request.url) @@ -361,6 +408,32 @@ def handle_route(route): if result: return resource_type = route.request.resource_type + # pass options + if route.request.method.lower() == "options": + route.continue_() + return + # mock response + request_mock_key_value = GlobalContext.get_global_cache("request_mock_key_value") + if request_mock_key_value is not None and len(request_mock_key_value) > 0: + try: + mock_rule = mock_rules(route.request.url, request_mock_key_value) + if mock_rule is not None: + log.info( + f"url:{route.request.url}===== match mock key:{mock_rule.get('key')} mock case " + f":{mock_rule.get('value')}===================") + mock_body = get_case_response_body(mock_rule.get("value")) + if mock_body: + if not isinstance(mock_body, str): + mock_body = json.dumps(mock_body) + route.fulfill(status=200, + content_type="application/json;charset=utf-8", + body=mock_body) + return + + except Exception as mock_error: + log.info("find mock info error", mock_error) + finally: + pass if resource_type != 'fetch' and resource_type != 'xhr': route.continue_() return diff --git a/flybirds/core/plugin/plugins/default/web/step.py b/flybirds/core/plugin/plugins/default/web/step.py index 4d59fcda..05b2b107 100644 --- a/flybirds/core/plugin/plugins/default/web/step.py +++ b/flybirds/core/plugin/plugins/default/web/step.py @@ -16,6 +16,7 @@ Interception as request_op from flybirds.core.exceptions import FlybirdsException from flybirds.utils import dsl_helper, uuid_helper +from flybirds.core.global_context import GlobalContext __open__ = ["Step"] @@ -355,8 +356,7 @@ def picture_compare_from_path(context, target_element, compared_picture_path): old_filename = f"{os.path.splitext(filename)[0]}_{uuid}_old.png" target_image_path = os.path.join(directory, old_filename) target_image_io.save(target_image_path) - request_op.compare_images(context,target_image_path, compared_picture_path, threshold) - + request_op.compare_images(context, target_image_path, compared_picture_path, threshold) @staticmethod def dom_ele_compare_from_path(context, target_ele, compared_text_path): @@ -375,3 +375,18 @@ def dom_ele_compare_from_path(context, target_ele, compared_text_path): @staticmethod def call_external_party_api(context, method, url, data, headers): request_op.call_external_party_api(method, url, data, headers) + + @staticmethod + def open_web_mock(context, service_str, mock_case_id_str): + request_mock_key_value = GlobalContext.get_global_cache("request_mock_key_value") + if request_mock_key_value is None: + request_mock_key_value = [] + GlobalContext.set_global_cache("request_mock_key_value", request_mock_key_value) + request_op.open_web_mock(service_str, mock_case_id_str, request_mock_key_value) + + @staticmethod + def remove_web_mock(context): + request_mock_key_value = GlobalContext.get_global_cache("request_mock_key_value") + if request_mock_key_value is not None: + del request_mock_key_value + GlobalContext.set_global_cache("request_mock_key_value", None) From 2a9faf0d63318768bba4e306c283ebf2b178de12 Mon Sep 17 00:00:00 2001 From: xingwenyang Date: Tue, 20 Jun 2023 17:29:59 +0800 Subject: [PATCH 2/2] remove log --- flybirds/core/plugin/plugins/default/web/page.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/flybirds/core/plugin/plugins/default/web/page.py b/flybirds/core/plugin/plugins/default/web/page.py index 2ef2dbf0..a84ad63f 100644 --- a/flybirds/core/plugin/plugins/default/web/page.py +++ b/flybirds/core/plugin/plugins/default/web/page.py @@ -387,8 +387,6 @@ def mock_rules(url: str, request_mock_key_value: list): if mock_find: match_mock_key = mock_rule mock_rule["max"] = mock_rule.get("max") - 1 - log.info( - f"{path} match mock case: key f{mock_rule.get('key')}----value {mock_rule.get('value')}") break return match_mock_key