网页UI界面自动化测试录制工具(Console控制台版),让python selenium和Appium自动化测试脚本开发更方便
一、背景
自动化测试中,QTP和selenium IDE都支持浏览器录制与回放功能,就像一个记录操作步骤的机器人,可以按照记录的步骤重新执行一遍,这就是脚本录制。
个人觉得传统录制工具有些弊端,加上要定制支持我自己的自动化框架(python单机版自动化测试框架源代码),所以自己用JavaScript写了一个录制工具,在浏览器控制台打印记录的python脚本(深绿色)如下:
二、使用说明:
使用步骤
1、复制下面(三)中的代码,可以直接在浏览器的控制台执行(代码内容在一行才能执行,多行会失败),执行成功后就可以开始录制网页操作(一般测试通过且可用才生成结果,减少每个脚本调试时间)。
2、录制后,在控制台执行如下命令,可以保存结果到文件test.txt。
my_download_file()
3、如果想清空之前录制的内容,在控制台执行如下命令,则下次保存结果文件test.txt就重新开始记录。
file_content = "";
注意事项
1、如果iframe和你的网页都来自同一个域,那么应该能够访问iframe的内容而不会遇到跨源问题。
2、如果iframe来自不同的源,暂时不支持。如果iframe的内容是由你控制的,你可以在服务器上设置CORS策略,允许特定的外部源访问资源。
3、如果有些情况下不会触发事件监听器,则在界面点击鼠标后,再用如下在浏览器控制台中执行尝试看是否可以获取单次操作的内容,如果还不行只能自己人工写自动化。
myDispose_click(document.activeElement);
目前了解到是打开了Toggle device toolbar会影响部分元素不会触发监听器。可能还有其它因素。
三、JavaScript代码如下:
let iframes = document.getElementsByTagName('iframe'); var click_textContent = ' var logLevel = 0;/*0:只打印结果,1:全日志*/ var father_level = 0; var child_ctl = ""; var child_ctl_tmp = ""; var first_focusedElement = null; var if_unique = ""; var file_content = ""; var myDisposeType = 1;/*1:普通,2:附近文本唯一,3:列表*/ window.clickedElement = null; document.addEventListener("click", function(event) { window.clickedElement = event.target; console.log(window.clickedElement); father_level = 0; if (myDisposeType === 3) { check_child_classes(window.clickedElement); } else if (myDisposeType === 2) { indirectly_find_element(window.clickedElement) } else { myDispose_click(window.clickedElement); } if (iframeType === 1) { if (window.parent && typeof window.parent.postMessage === "function") { window.parent.postMessage({ key: "file_content", value: file_content }, "*"); file_content = ""; } } }, true); function my_type(typeNum=1) { for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; iframe.contentWindow.myDisposeType = typeNum; } myDisposeType = typeNum; if (typeNum === 1) { console.log("模式:普通"); } else if (typeNum === 2) { console.log("模式:检查附近节点唯一"); } else if (typeNum === 3) { console.log("模式:检查类表格"); } else { console.log("模式类型输入错误"); } return true; } function my_logLevel(typeNum=0) { for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; iframe.contentWindow.logLevel = typeNum; } logLevel = typeNum; if (typeNum === 0) { console.log("日志模式:只打印结果"); } else if (typeNum === 1) { console.log("日志模式:打印全日志"); } else { console.log("日志模式类型输入错误"); } return true; } function myDispose_click(focusedElement) { console.log(`开始 父${father_level} -------------------------`); let tag_name = focusedElement.tagName.toLowerCase(); let outerHTML = focusedElement.outerHTML; /*console_log_debug(outerHTML);*/ let my_all_value = ""; let text = focusedElement.textContent.trim().replace(/"/g, "\\\""); if (father_level === 0) { console_log_debug("$$$$$$$$$$$$$$$$$$$$$$"); let placeholderValue = focusedElement.getAttribute("placeholder"); if (placeholderValue === null) { placeholderValue = ""; } console.log(text+placeholderValue); file_content = file_content + "\\n\\n# " + text + placeholderValue; myDispose_SelectorXPath(focusedElement); } if (tag_name === "body") { if (father_level === 0) { console.log("没有找到元素"); console_log_debug(`结束 父${father_level} -------------------------`); return null } else { if (first_focusedElement === null) { return myDispose_fullXPath(focusedElement); } else { return myDispose_fullXPath(first_focusedElement); } } } if (text !== "" && !text.includes("\\n")) { if (father_level === 0) { let xpath = `//${tag_name}[text()=\'${text}\']`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } my_all_value = `contains(text(),\'${text}\')`; if (myDispose_count_number(text, "text", tag_name)) { let xpath = `//${tag_name}[${my_all_value}]`; let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } else { text = ""; } } else { text = ""; } /*let attributes = focusedElement.attributes;*/ let attributes = sortAttributes(focusedElement); console_log_debug(`属性名称列表: ${Array.from(attributes).map(attr => attr.name).join(",")}`); for (let i = 0; i < attributes.length; i++) { let attribute_name = attributes[i].name; let attribute_value = attributes[i].value; if (attribute_value === "" || typeof attribute_value === "undefined") { continue; } if (attribute_name === "class") { if (tag_name === "svg") { let class_value_list = attribute_value.split(" "); console_log_debug(`class列表:${class_value_list}`); if (class_value_list.includes("focusing")) { class_value_list = class_value_list.filter(value => value !== "focusing"); } for (let class_value of class_value_list) { if (class_value === "") { continue; } if (my_all_value === "") { my_all_value = `contains(@class,\'${class_value}\')`; } else { my_all_value += ` and contains(@class,\'${class_value}\')`; } let xpath = `//*[name()=\'svg\' and contains(@class,\'${class_value}\')]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } if (text !== "") { xpath = `//*[name()=\'svg\' and contains(text(),\'${text}\') and contains(@class,\'${class_value}\')]`; result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } xpath = `//*[name()=\'svg\' and ${my_all_value}]`; result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } else { let class_value_list = attribute_value.split(" "); console_log_debug(`class列表:${class_value_list}`); if (class_value_list.includes("focusing")) { class_value_list = class_value_list.filter(value => value !== "focusing"); } for (let class_value of class_value_list) { if (class_value === "") { continue; } if (my_all_value === "") { my_all_value = `contains(@class,\'${class_value}\')`; } else { my_all_value += ` and contains(@class,\'${class_value}\')`; } if (myDispose_count_number(class_value, attribute_name, tag_name)) { let parameter = `driver, By.CLASS_NAME, "${class_value}"`; myDispose_success(parameter); return parameter; } if (text !== "") { let xpath = `//${tag_name}[contains(text(),\'${text}\') and contains(@class,\'${class_value}\')]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } let xpath = `//${tag_name}[${my_all_value}]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } } else { console_log_debug(`${attribute_name}:${attribute_value}`); if (attribute_name.includes("xlink:") || (attribute_name !== "src" && attribute_value.match(/[0-9]/))) { continue; } if (my_all_value === "") { my_all_value = `contains(@${attribute_name}, \'${attribute_value}\')`; } else { my_all_value += ` and contains(@${attribute_name}, \'${attribute_value}\')`; } if (myDispose_count_number(attribute_value, attribute_name, tag_name)) { let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(@${attribute_name}, \'${attribute_value}\')]`; } let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } if (text !== "") { let xpath; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and contains(text(),\'${text}\') and contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(text(),\'${text}\') and contains(@${attribute_name}, \'${attribute_value}\')]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and ${my_all_value}]`; } else { xpath = `//${tag_name}[${my_all_value}]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } if (my_all_value !== "" && attributes.length !==0) { let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and ${my_all_value}]`; } else { xpath = `//${tag_name}[${my_all_value}]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } else { myDispose_not_unique(focusedElement, xpath); } } else { let father = focusedElement.parentElement; if (father) { if (father.children.length === 1) { let xpath = `//${tag_name}`; myDispose_not_unique(focusedElement, xpath); } else { if (first_focusedElement === null) { return myDispose_fullXPath(focusedElement); } else { return myDispose_fullXPath(first_focusedElement); } } } else { return null; } } } function sortAttributes(element) { const specificAttrs = ["id", "name", "placeholder", "class", "src"]; const specificAttrValues = specificAttrs.map(attr => { const attribute = element.getAttribute(attr); return { name: attr, value: attribute }; }).filter(attr => attr.value !== null); const otherAttrs = Array.from(element.attributes).filter(attr => !specificAttrs.includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name)); return [...specificAttrValues, ...otherAttrs].map(attr => ({ name: attr.name, value: attr.value || attr.nodeValue })); } function myDispose_success(parameter) { if (father_level === 0) { let tmp = `self.myWtClickEx(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp); file_content = file_content + "\\n" + tmp; } else { if (if_unique === "") { let tmp_f = `${if_unique}father = self.myWtFindElement(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp_f); let tmp_s = `${if_unique}self.myWtClickEx(${child_ctl})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp_s); file_content = file_content + "\\n" + tmp_f + "\\n" + tmp_s; } else { let tmp_f = `${if_unique}father = self.myWtFindElement(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn(tmp_f); let tmp_s = `${if_unique}self.myWtClickEx(${child_ctl})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn(tmp_s); } } if_unique = ""; console_log_debug(`结束 父${father_level} -------------------------`); } function myDispose_count_evaluate(xpath) { let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (elements.snapshotLength === 1) { if (father_level === 0) { console_log_debug(`"${xpath}"在网页中出现1次`); return true; } else { let firstElement = elements.snapshotItem(0); let result = document.evaluate(child_ctl_tmp, firstElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (result.snapshotLength === 1) { console_log_debug(`"${xpath}"在网页中出现1次`); return true; } else { part_xpath = getElementfullXPath(first_focusedElement, firstElement); full_xpath = xpath + part_xpath; let childs = document.evaluate(full_xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (childs.snapshotLength === 1) { let parameter = `driver, By.XPATH, "${full_xpath}"`; let tmp = `self.myWtClickEx(${parameter})`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; console_log_debug(`"${full_xpath}"在网页中出现1次`); if_unique = "# 不是唯1 "; return true; } else { console_log_debug(`"${full_xpath}"在网页中出现${childs.snapshotLength}次`); return null; } } } } else { console_log_debug(`"${xpath}"在网页中出现${elements.snapshotLength}次`); return null; } } function myDispose_count_number(attribute_value, attribute_name, tag_name) { if (attribute_value === "") { return null; }; if (attribute_name !== "text" && attribute_name !== "src" && attribute_value.match(/[0-9]/)) { return null; }; let xpath; if (attribute_name !== "text") { xpath = `//${tag_name}[contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(text(), \'${attribute_value}\')]`; }; let result = myDispose_count_evaluate(xpath); if (result) { return true; } else { return null; }; } function myDispose_not_unique(focusedElement, xpath) { let textStr = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn("# 不是唯1 " + textStr); console_log_debug(`结束 父${father_level} -------------------------`); if (father_level === 0) { child_ctl = `father, By.XPATH, ".${xpath}"`; child_ctl_tmp = `.${xpath}`; first_focusedElement = focusedElement; } let father = focusedElement.parentElement; if (father) { father_level++; myDispose_click(father); } } function myDispose_fullXPath(element) { let xpath = getElementfullXPath(element); let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (elements && elements.snapshotLength === 1) { let tmp = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; console_log_debug(`结束 父${father_level} -------------------------`); return xpath; } else if (elements && elements.snapshotLength > 1) { console_log_warn(`# 不是唯1 self.myWtClickEx(driver, By.XPATH, "${xpath}")`); console_log_debug(`结束 父${father_level} -------------------------`); return null; } else { console_log_warn(`# 没找到 self.myWtClickEx(driver, By.XPATH, "${xpath}")`); console_log_debug(`结束 父${father_level} -------------------------`); return null; } } function getElementfullXPath(element, ancestor=null) { let if_continue = 1; if (element && element.id) if (!element.id.match(/[0-9]/)) { return \'//*[@id="\' + element.id + \'"]\'; } if (element==null) return ""; let index = 0; let loacl_tagName = element.tagName; let sibling = element.previousElementSibling; let sibling_tagName = null; if (sibling) { sibling_tagName = sibling.tagName; } while (sibling && loacl_tagName === sibling_tagName) { index++; sibling = sibling.previousElementSibling; if (sibling) { sibling_tagName = sibling.tagName; } else { sibling_tagName = null; } } parent = element.parentNode; if (parent) { if (ancestor === parent) { if_continue = 0; } if (if_continue === 1) { var xpath = getElementfullXPath(parent, ancestor); if (xpath === "undefined") { return ""; } else { if (element.tagName.toLowerCase() === "svg") { xpath += "/*[name()=\'svg\']"; } else { xpath += "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]"; } return xpath; } } else { if (xpath === "undefined") { return ""; } else { if (element.tagName.toLowerCase() === "svg") { xpath = "/*[name()=\'svg\']"; } else { xpath = "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]"; } return xpath; } } } else { return ""; } } function myDispose_SelectorXPath(element) { let selectorXPath = getSelectorPath(element); console_log_debug(selectorXPath); let elements = document.querySelectorAll(selectorXPath); if (elements && elements.length === 1) { let tmp = `self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; return selectorXPath; } else if (elements && elements.length > 1) { console_log_warn(`# 不是唯1 self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`); return null; } else { console_log_warn(`# 没找到 self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`); return null; } } function getSelectorPath(element) { if (element.tagName === "HTML") { return ""; } let selector = element.tagName.toLowerCase(); if (element.id) { return `${selector}#${element.id}`.replace(".", "\\\\."); } var className = element.className; if (typeof className === \'string\' && className.trim().length > 0) { selector += \'.\' + className.trim().replace(/ +/g, "."); } const parentSelector = getSelectorPath(element.parentNode); return parentSelector ? `${parentSelector} > ${selector}` : selector; } function my_download_file(fileName="test.txt") { if (file_content === "") { console.log("没有内容需要保存"); return null; } file_content = file_content.replace(/\\n\\n/, "\\n"); const a = document.createElement("a"); const blob = new Blob([file_content], { type: "text/plain" }); const url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); file_content = ""; return true; } function console_log_debug(message) { if (logLevel === 1) { console.log(message); } } function console_log_info(message) { console.log(`%c${message}`, "color: darkgreen;"); } function console_log_warn(message) { console.log(`%c${message}`, "color: #8b860b;"); }; '; var click_sibling_textContent = ' /*检查是否存在类似表格*/ function check_child_classes(element, level = 0) { console.log(`开始 父${level} -------------------------`); console.log(element); /*检查第一级子节点*/ let parentNode = element.parentNode; if (parentNode) { var firstLevelChildren = Array.from(parentNode.children); var firstLevelCheckResult = checkSiblingClasses(firstLevelChildren); if (firstLevelCheckResult.areSame) { console.log(`父${level}级的class相同: ${firstLevelCheckResult.className}`); myDispose_click(parentNode); replace_myWtClickEx(); return check_child_classes_success(firstLevelCheckResult.className); } /* 检查第二级子节点*/ const secondLevelClasses = {}; let childrenNum = 0; firstLevelChildren.forEach(firstLevelChild => { Array.from(firstLevelChild.children).forEach(secondLevelChild => { const childClass = secondLevelChild.className; if (!secondLevelClasses[childClass]) { secondLevelClasses[childClass] = 0; } secondLevelClasses[childClass]++; }); childrenNum++; }); const uniqueSecondLevelClasses = Object.keys(secondLevelClasses); if (uniqueSecondLevelClasses.length === 1 && childrenNum > 1) { console.log(`父${level}级的子节点class相同: ${uniqueSecondLevelClasses[0]}`); myDispose_click(parentNode); replace_myWtClickEx(); return check_child_classes_success(uniqueSecondLevelClasses[0]); } check_child_classes(parentNode, level + 1); } return; } function replace_myWtClickEx() { let lastIndex = file_content.lastIndexOf("\\n# "); if (lastIndex !== -1) { let suffix = file_content.slice(lastIndex + 2); let replacedSuffix = suffix.replace(/self\.myWtClickEx/g, "self.myWtFindElement"); file_content = file_content.slice(0, lastIndex + 2) + replacedSuffix; } } function check_child_classes_success(classes) { let tmp = `class_list = self.myWtFindElements(driver, By.CLASS_NAME, \'${classes}\')\\nfor elt in class_list:\\n\\telt.text`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; return tmp; } /*辅助函数,用于检查子节点的class是否一致*/ function checkSiblingClasses(siblings) { if (siblings.length <= 1) { return { areSame: false, className: "" }; /*子节点数量不多于一个,认为无需比较*/ } var firstClass = siblings[0].className; if (firstClass === "") { return { areSame: false, className: "" }; } var areSame = siblings.every(sibling => sibling.className === firstClass); return { areSame, className: firstClass }; } /*取元素父节点下所有子节点,检查是否唯一*/ function indirectly_find_element(element, class_xpath = "", level=1) { let parentNode = element.parentNode; let allDescendants = findAllDescendants(parentNode); for (let elt of allDescendants) { let text = elt[0].textContent.trim().replace(/"/g, "\\\""); if (text.match(/[0-9]/)) { continue; } let level = generateDots(elt[1]); if (text !== "" && !text.includes("\\n")) { let tmp = `contains(text(),\'${text}\')`; let tag_name = elt[0].tagName.toLowerCase(); if (myDispose_count_number(text, "text", tag_name)) { class_xpath = get_local_element_xpath(element) + class_xpath; let xpath = `//${tag_name}[${tmp}]${level}${class_xpath}`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; file_content = file_content + "\\n\\n# " + text; myDispose_success(parameter); return parameter; } } } } class_xpath = get_local_element_xpath(element) + class_xpath; return indirectly_find_element(parentNode, class_xpath, level+1); } function get_local_element_xpath(element) { if (element) { let xpath = ""; let tagName = element.tagName.toLowerCase(); let classValue = element.className.split(" ")[0]; if (classValue === "") { xpath = `/${tagName}`; } else { xpath = `/${tagName}[contains(@class,\'${classValue}\')]`; } return xpath; } return ""; } function generateDots(num) { let result = ""; for (let i = 0; i < num; i++) { result += "/.."; } return result; } /*使用递归函数查找所有子元素*/ function findAllDescendants(node, level = 1) { let descendants = []; if (node.childNodes && node.childNodes.length > 0) { for (let childNode of node.childNodes) { /*只考虑ELEMENT_NODE类型的节点*/ if (childNode.nodeType === 1) { descendants.push([childNode, level]); if (childNode.childNodes && childNode.childNodes.length > 0) { descendants = descendants.concat(findAllDescendants(childNode, level+1)); } } } } return descendants; } '; var message_textContent = ' window.addEventListener("message", function(event) { if (event.data.key === "file_content") { let message_value = event.data.value; file_content = file_content + message_value; } }, false); '; var input_textContent = ' let inputs = document.querySelectorAll(`input[type="text"]`); inputs.forEach(input => { input.addEventListener("input", function(event) { console_log_debug(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; file_content = file_content + "\\n" + textStr; console_log_info(textStr); } }, true); }); '; var not_iframe_textContent = ' var iframeType = 0; '; var iframe_textContent = ' var iframeType = 1; '; /*iframe*/ for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; if (iframe.contentWindow && iframe.contentWindow.document) { let script = iframe.contentWindow.document.createElement('script'); script.type = 'text/javascript'; script.textContent = iframe_textContent + click_textContent + click_sibling_textContent; iframe.contentWindow.document.head.appendChild(script); let inputs = iframe.contentWindow.document.querySelectorAll(`input[type="text"]`); inputs.forEach(input => { input.addEventListener("input", function(event) { console_log_debug(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; file_content = file_content + "\\n" + textStr; console_log_info(textStr); if (window.parent && typeof window.parent.postMessage === "function") { window.parent.postMessage({ key: "file_content", value: file_content }, "*"); file_content = ""; } } }, true); }); }; } /*非iframe*/ let script = document.createElement('script'); script.type = 'text/javascript'; script.textContent = not_iframe_textContent + click_textContent + click_sibling_textContent + input_textContent + message_textContent; document.head.appendChild(script);
四、JavaScript原始缩进版代码如下(完善中):
let iframes = document.getElementsByTagName('iframe'); var click_textContent = ' var logLevel = 0;/*0:只打印结果,1:全日志*/ var father_level = 0; var child_ctl = ""; var child_ctl_tmp = ""; var first_focusedElement = null; var if_unique = ""; var file_content = ""; var myDisposeType = 1;/*1:普通,2:附近文本唯一,3:列表*/ window.clickedElement = null; document.addEventListener("click", function(event) { window.clickedElement = event.target; console.log(window.clickedElement); father_level = 0; if (myDisposeType === 3) { check_child_classes(window.clickedElement); } else if (myDisposeType === 2) { indirectly_find_element(window.clickedElement) } else { myDispose_click(window.clickedElement); } if (iframeType === 1) { if (window.parent && typeof window.parent.postMessage === "function") { window.parent.postMessage({ key: "file_content", value: file_content }, "*"); file_content = ""; } } }, true); function my_type(typeNum=1) { for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; iframe.contentWindow.myDisposeType = typeNum; } myDisposeType = typeNum; if (typeNum === 1) { console.log("模式:普通"); } else if (typeNum === 2) { console.log("模式:检查附近节点唯一"); } else if (typeNum === 3) { console.log("模式:检查类表格"); } else { console.log("模式类型输入错误"); } return true; } function my_logLevel(typeNum=0) { for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; iframe.contentWindow.logLevel = typeNum; } logLevel = typeNum; if (typeNum === 0) { console.log("日志模式:只打印结果"); } else if (typeNum === 1) { console.log("日志模式:打印全日志"); } else { console.log("日志模式类型输入错误"); } return true; } function myDispose_click(focusedElement) { console.log(`开始 父${father_level} -------------------------`); let tag_name = focusedElement.tagName.toLowerCase(); let outerHTML = focusedElement.outerHTML; /*console_log_debug(outerHTML);*/ let my_all_value = ""; let text = focusedElement.textContent.trim().replace(/"/g, "\\\""); if (father_level === 0) { console_log_debug("$$$$$$$$$$$$$$$$$$$$$$"); let placeholderValue = focusedElement.getAttribute("placeholder"); if (placeholderValue === null) { placeholderValue = ""; } console.log(text+placeholderValue); file_content = file_content + "\\n\\n# " + text + placeholderValue; myDispose_SelectorXPath(focusedElement); } if (tag_name === "body") { if (father_level === 0) { console.log("没有找到元素"); console_log_debug(`结束 父${father_level} -------------------------`); return null } else { if (first_focusedElement === null) { return myDispose_fullXPath(focusedElement); } else { return myDispose_fullXPath(first_focusedElement); } } } if (text !== "" && !text.includes("\\n")) { if (father_level === 0) { let xpath = `//${tag_name}[text()=\'${text}\']`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } my_all_value = `contains(text(),\'${text}\')`; if (myDispose_count_number(text, "text", tag_name)) { let xpath = `//${tag_name}[${my_all_value}]`; let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } else { text = ""; } } else { text = ""; } /*let attributes = focusedElement.attributes;*/ let attributes = sortAttributes(focusedElement); console_log_debug(`属性名称列表: ${Array.from(attributes).map(attr => attr.name).join(",")}`); for (let i = 0; i < attributes.length; i++) { let attribute_name = attributes[i].name; let attribute_value = attributes[i].value; if (attribute_value === "" || typeof attribute_value === "undefined") { continue; } if (attribute_name === "class") { if (tag_name === "svg") { let class_value_list = attribute_value.split(" "); console_log_debug(`class列表:${class_value_list}`); if (class_value_list.includes("focusing")) { class_value_list = class_value_list.filter(value => value !== "focusing"); } for (let class_value of class_value_list) { if (class_value === "") { continue; } if (my_all_value === "") { my_all_value = `contains(@class,\'${class_value}\')`; } else { my_all_value += ` and contains(@class,\'${class_value}\')`; } let xpath = `//*[name()=\'svg\' and contains(@class,\'${class_value}\')]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } if (text !== "") { xpath = `//*[name()=\'svg\' and contains(text(),\'${text}\') and contains(@class,\'${class_value}\')]`; result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } xpath = `//*[name()=\'svg\' and ${my_all_value}]`; result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } else { let class_value_list = attribute_value.split(" "); console_log_debug(`class列表:${class_value_list}`); if (class_value_list.includes("focusing")) { class_value_list = class_value_list.filter(value => value !== "focusing"); } for (let class_value of class_value_list) { if (class_value === "") { continue; } if (my_all_value === "") { my_all_value = `contains(@class,\'${class_value}\')`; } else { my_all_value += ` and contains(@class,\'${class_value}\')`; } if (myDispose_count_number(class_value, attribute_name, tag_name)) { let parameter = `driver, By.CLASS_NAME, "${class_value}"`; myDispose_success(parameter); return parameter; } if (text !== "") { let xpath = `//${tag_name}[contains(text(),\'${text}\') and contains(@class,\'${class_value}\')]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } let xpath = `//${tag_name}[${my_all_value}]`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } } else { console_log_debug(`${attribute_name}:${attribute_value}`); if (attribute_name.includes("xlink:") || (attribute_name !== "src" && attribute_value.match(/[0-9]/))) { continue; } if (my_all_value === "") { my_all_value = `contains(@${attribute_name}, \'${attribute_value}\')`; } else { my_all_value += ` and contains(@${attribute_name}, \'${attribute_value}\')`; } if (myDispose_count_number(attribute_value, attribute_name, tag_name)) { let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(@${attribute_name}, \'${attribute_value}\')]`; } let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } if (text !== "") { let xpath; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and contains(text(),\'${text}\') and contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(text(),\'${text}\') and contains(@${attribute_name}, \'${attribute_value}\')]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and ${my_all_value}]`; } else { xpath = `//${tag_name}[${my_all_value}]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } if (my_all_value !== "" && attributes.length !==0) { let xpath = ""; if (tag_name === "svg") { xpath = `//*[name()=\'svg\' and ${my_all_value}]`; } else { xpath = `//${tag_name}[${my_all_value}]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } else { myDispose_not_unique(focusedElement, xpath); } } else { let father = focusedElement.parentElement; if (father) { if (father.children.length === 1) { let xpath = `//${tag_name}`; myDispose_not_unique(focusedElement, xpath); } else { if (first_focusedElement === null) { return myDispose_fullXPath(focusedElement); } else { return myDispose_fullXPath(first_focusedElement); } } } else { return null; } } } function sortAttributes(element) { const specificAttrs = ["id", "name", "placeholder", "class", "src"]; const specificAttrValues = specificAttrs.map(attr => { const attribute = element.getAttribute(attr); return { name: attr, value: attribute }; }).filter(attr => attr.value !== null); const otherAttrs = Array.from(element.attributes).filter(attr => !specificAttrs.includes(attr.name)).sort((a, b) => a.name.localeCompare(b.name)); return [...specificAttrValues, ...otherAttrs].map(attr => ({ name: attr.name, value: attr.value || attr.nodeValue })); } function myDispose_success(parameter) { if (father_level === 0) { let tmp = `self.myWtClickEx(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp); file_content = file_content + "\\n" + tmp; } else { if (if_unique === "") { let tmp_f = `${if_unique}father = self.myWtFindElement(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp_f); let tmp_s = `${if_unique}self.myWtClickEx(${child_ctl})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_info(tmp_s); file_content = file_content + "\\n" + tmp_f + "\\n" + tmp_s; } else { let tmp_f = `${if_unique}father = self.myWtFindElement(${parameter})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn(tmp_f); let tmp_s = `${if_unique}self.myWtClickEx(${child_ctl})`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn(tmp_s); } } if_unique = ""; console_log_debug(`结束 父${father_level} -------------------------`); } function myDispose_count_evaluate(xpath) { let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (elements.snapshotLength === 1) { if (father_level === 0) { console_log_debug(`"${xpath}"在网页中出现1次`); return true; } else { let firstElement = elements.snapshotItem(0); let result = document.evaluate(child_ctl_tmp, firstElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (result.snapshotLength === 1) { console_log_debug(`"${xpath}"在网页中出现1次`); return true; } else { part_xpath = getElementfullXPath(first_focusedElement, firstElement); full_xpath = xpath + part_xpath; let childs = document.evaluate(full_xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (childs.snapshotLength === 1) { let parameter = `driver, By.XPATH, "${full_xpath}"`; let tmp = `self.myWtClickEx(${parameter})`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; console_log_debug(`"${full_xpath}"在网页中出现1次`); if_unique = "# 不是唯1 "; return true; } else { console_log_debug(`"${full_xpath}"在网页中出现${childs.snapshotLength}次`); return null; } } } } else { console_log_debug(`"${xpath}"在网页中出现${elements.snapshotLength}次`); return null; } } function myDispose_count_number(attribute_value, attribute_name, tag_name) { if (attribute_value === "") { return null; }; if (attribute_name !== "text" && attribute_name !== "src" && attribute_value.match(/[0-9]/)) { return null; }; let xpath; if (attribute_name !== "text") { xpath = `//${tag_name}[contains(@${attribute_name}, \'${attribute_value}\')]`; } else { xpath = `//${tag_name}[contains(text(), \'${attribute_value}\')]`; }; let result = myDispose_count_evaluate(xpath); if (result) { return true; } else { return null; }; } function myDispose_not_unique(focusedElement, xpath) { let textStr = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`.replace(\'svg[\', \'*[name()=\"svg\" and \'); console_log_warn("# 不是唯1 " + textStr); console_log_debug(`结束 父${father_level} -------------------------`); if (father_level === 0) { child_ctl = `father, By.XPATH, ".${xpath}"`; child_ctl_tmp = `.${xpath}`; first_focusedElement = focusedElement; } let father = focusedElement.parentElement; if (father) { father_level++; myDispose_click(father); } } function myDispose_fullXPath(element) { let xpath = getElementfullXPath(element); let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (elements && elements.snapshotLength === 1) { let tmp = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; console_log_debug(`结束 父${father_level} -------------------------`); return xpath; } else if (elements && elements.snapshotLength > 1) { console_log_warn(`# 不是唯1 self.myWtClickEx(driver, By.XPATH, "${xpath}")`); console_log_debug(`结束 父${father_level} -------------------------`); return null; } else { console_log_warn(`# 没找到 self.myWtClickEx(driver, By.XPATH, "${xpath}")`); console_log_debug(`结束 父${father_level} -------------------------`); return null; } } function getElementfullXPath(element, ancestor=null) { let if_continue = 1; if (element && element.id) if (!element.id.match(/[0-9]/)) { return \'//*[@id="\' + element.id + \'"]\'; } if (element==null) return ""; let index = 0; let loacl_tagName = element.tagName; let sibling = element.previousElementSibling; let sibling_tagName = null; if (sibling) { sibling_tagName = sibling.tagName; } while (sibling && loacl_tagName === sibling_tagName) { index++; sibling = sibling.previousElementSibling; if (sibling) { sibling_tagName = sibling.tagName; } else { sibling_tagName = null; } } parent = element.parentNode; if (parent) { if (ancestor === parent) { if_continue = 0; } if (if_continue === 1) { var xpath = getElementfullXPath(parent, ancestor); if (xpath === "undefined") { return ""; } else { if (element.tagName.toLowerCase() === "svg") { xpath += "/*[name()=\'svg\']"; } else { xpath += "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]"; } return xpath; } } else { if (xpath === "undefined") { return ""; } else { if (element.tagName.toLowerCase() === "svg") { xpath = "/*[name()=\'svg\']"; } else { xpath = "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]"; } return xpath; } } } else { return ""; } } function myDispose_SelectorXPath(element) { let selectorXPath = getSelectorPath(element); console_log_debug(selectorXPath); let elements = document.querySelectorAll(selectorXPath); if (elements && elements.length === 1) { let tmp = `self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; return selectorXPath; } else if (elements && elements.length > 1) { console_log_warn(`# 不是唯1 self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`); return null; } else { console_log_warn(`# 没找到 self.myWtClickEx(driver, By.CSS_SELECTOR, "${selectorXPath}")`); return null; } } function getSelectorPath(element) { if (element.tagName === "HTML") { return ""; } let selector = element.tagName.toLowerCase(); if (element.id) { return `${selector}#${element.id}`.replace(".", "\\\\."); } var className = element.className; if (typeof className === \'string\' && className.trim().length > 0) { selector += \'.\' + className.trim().replace(/ +/g, "."); } const parentSelector = getSelectorPath(element.parentNode); return parentSelector ? `${parentSelector} > ${selector}` : selector; } function my_download_file(fileName="test.txt") { if (file_content === "") { console.log("没有内容需要保存"); return null; } file_content = file_content.replace(/\\n\\n/, "\\n"); const a = document.createElement("a"); const blob = new Blob([file_content], { type: "text/plain" }); const url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); file_content = ""; return true; } function console_log_debug(message) { if (logLevel === 1) { console.log(message); } } function console_log_info(message) { console.log(`%c${message}`, "color: darkgreen;"); } function console_log_warn(message) { console.log(`%c${message}`, "color: #8b860b;"); }; '; var click_sibling_textContent = ' /*检查是否存在类似表格*/ function check_child_classes(element, level = 0) { console.log(`开始 父${level} -------------------------`); console.log(element); /*检查第一级子节点*/ let parentNode = element.parentNode; if (parentNode) { var firstLevelChildren = Array.from(parentNode.children); var firstLevelCheckResult = checkSiblingClasses(firstLevelChildren); if (firstLevelCheckResult.areSame) { console.log(`父${level}级的class相同: ${firstLevelCheckResult.className}`); myDispose_click(parentNode); replace_myWtClickEx(); return check_child_classes_success(firstLevelCheckResult.className); } /* 检查第二级子节点*/ const secondLevelClasses = {}; let childrenNum = 0; firstLevelChildren.forEach(firstLevelChild => { Array.from(firstLevelChild.children).forEach(secondLevelChild => { const childClass = secondLevelChild.className; if (!secondLevelClasses[childClass]) { secondLevelClasses[childClass] = 0; } secondLevelClasses[childClass]++; }); childrenNum++; }); const uniqueSecondLevelClasses = Object.keys(secondLevelClasses); if (uniqueSecondLevelClasses.length === 1 && childrenNum > 1) { console.log(`父${level}级的子节点class相同: ${uniqueSecondLevelClasses[0]}`); myDispose_click(parentNode); replace_myWtClickEx(); return check_child_classes_success(uniqueSecondLevelClasses[0]); } check_child_classes(parentNode, level + 1); } return; } function replace_myWtClickEx() { let lastIndex = file_content.lastIndexOf("\\n# "); if (lastIndex !== -1) { let suffix = file_content.slice(lastIndex + 2); let replacedSuffix = suffix.replace(/self\.myWtClickEx/g, "self.myWtFindElement"); file_content = file_content.slice(0, lastIndex + 2) + replacedSuffix; } } function check_child_classes_success(classes) { let tmp = `class_list = self.myWtFindElements(driver, By.CLASS_NAME, \'${classes}\')\\nfor elt in class_list:\\n\\telt.text`; console_log_info(tmp); file_content = file_content + "\\n" + tmp; return tmp; } /*辅助函数,用于检查子节点的class是否一致*/ function checkSiblingClasses(siblings) { if (siblings.length <= 1) { return { areSame: false, className: "" }; /*子节点数量不多于一个,认为无需比较*/ } var firstClass = siblings[0].className; if (firstClass === "") { return { areSame: false, className: "" }; } var areSame = siblings.every(sibling => sibling.className === firstClass); return { areSame, className: firstClass }; } /*取元素父节点下所有子节点,检查是否唯一*/ function indirectly_find_element(element, class_xpath = "", level=1) { let parentNode = element.parentNode; let allDescendants = findAllDescendants(parentNode); for (let elt of allDescendants) { let text = elt[0].textContent.trim().replace(/"/g, "\\\""); if (text.match(/[0-9]/)) { continue; } let level = generateDots(elt[1]); if (text !== "" && !text.includes("\\n")) { let tmp = `contains(text(),\'${text}\')`; let tag_name = elt[0].tagName.toLowerCase(); if (myDispose_count_number(text, "text", tag_name)) { class_xpath = get_local_element_xpath(element) + class_xpath; let xpath = `//${tag_name}[${tmp}]${level}${class_xpath}`; let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; file_content = file_content + "\\n\\n# " + text; myDispose_success(parameter); return parameter; } } } } class_xpath = get_local_element_xpath(element) + class_xpath; return indirectly_find_element(parentNode, class_xpath, level+1); } function get_local_element_xpath(element) { if (element) { let xpath = ""; let tagName = element.tagName.toLowerCase(); let classValue = element.className.split(" ")[0]; if (classValue === "") { xpath = `/${tagName}`; } else { xpath = `/${tagName}[contains(@class,\'${classValue}\')]`; } return xpath; } return ""; } function generateDots(num) { let result = ""; for (let i = 0; i < num; i++) { result += "/.."; } return result; } /*使用递归函数查找所有子元素*/ function findAllDescendants(node, level = 1) { let descendants = []; if (node.childNodes && node.childNodes.length > 0) { for (let childNode of node.childNodes) { /*只考虑ELEMENT_NODE类型的节点*/ if (childNode.nodeType === 1) { descendants.push([childNode, level]); if (childNode.childNodes && childNode.childNodes.length > 0) { descendants = descendants.concat(findAllDescendants(childNode, level+1)); } } } } return descendants; } '; var message_textContent = ' window.addEventListener("message", function(event) { if (event.data.key === "file_content") { let message_value = event.data.value; file_content = file_content + message_value; } }, false); '; var input_textContent = ' let inputs = document.querySelectorAll(`input[type="text"]`); inputs.forEach(input => { input.addEventListener("input", function(event) { console_log_debug(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; file_content = file_content + "\\n" + textStr; console_log_info(textStr); } }, true); }); '; var not_iframe_textContent = ' var iframeType = 0; '; var iframe_textContent = ' var iframeType = 1; '; /*iframe*/ for (let i = 0; i < iframes.length; i++) { let iframe = iframes[i]; if (iframe.contentWindow && iframe.contentWindow.document) { let script = iframe.contentWindow.document.createElement('script'); script.type = 'text/javascript'; script.textContent = iframe_textContent + click_textContent + click_sibling_textContent; iframe.contentWindow.document.head.appendChild(script); let inputs = iframe.contentWindow.document.querySelectorAll(`input[type="text"]`); inputs.forEach(input => { input.addEventListener("input", function(event) { console_log_debug(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; file_content = file_content + "\\n" + textStr; console_log_info(textStr); if (window.parent && typeof window.parent.postMessage === "function") { window.parent.postMessage({ key: "file_content", value: file_content }, "*"); file_content = ""; } } }, true); }); }; } /*非iframe*/ let script = document.createElement('script'); script.type = 'text/javascript'; script.textContent = not_iframe_textContent + click_textContent + click_sibling_textContent + input_textContent + message_textContent; document.head.appendChild(script);