自動化測試中,QTP和selenium IDE都支援瀏覽器錄製與回放功能,簡單的來說就像一個記錄操作步驟的機器人,可以按照記錄的步驟重新執行一遍,這就是指令碼錄製。
個人覺得傳統錄製工具有些弊端,加上要定製支援我自己的自動化框架(python單機版自動化測試框架原始碼),所以自己用javascript寫了一個錄製工具,在控制檯列印記錄的python指令碼如下:
程式碼如下(初稿,還在不斷除錯完善中):
var click_textContent = ' var father_level = 0; var child_ctl = ""; var child_ctl_tmp = ""; var next_focusedElement = null; window.clickedElement = null; document.addEventListener("click", function(event) { window.clickedElement = event.target; console.log(window.clickedElement); father_level = 0; myDispose_click(window.clickedElement); }); function myDispose_click(focusedElement) { console.log(`開始 父${father_level} -------------------------`); let tag_name = focusedElement.tagName.toLowerCase(); let outerHTML = focusedElement.outerHTML; console.log(outerHTML); if (tag_name === "body") { let xpath = getElementfullXPath(next_focusedElement); let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (elements && elements.snapshotLength === 1) { console.log(`self.myWtClickEx(driver, By.XPATH, "${xpath}")`); console.log("結束:tag名稱為body"); console.log(`結束 父${father_level} -------------------------`); return xpath; } else { console.log("結束:tag名稱為body"); console.log(`結束 父${father_level} -------------------------`); return null; } } let my_all_value = ""; let text = focusedElement.textContent.trim().replace(/"/g, "\\\""); if (text !== "" && !text.includes("\\n")) { 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 = "" } let attributes = focusedElement.attributes; console.log(`屬性名稱列表: ${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_name === "class") { let class_value_list = attribute_value.split(" "); console.log(`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 (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; } let xpath = ""; if (text === "") { xpath = `//${tag_name}[${my_all_value}]`; } else { xpath = `//${tag_name}[contains(text(),\'${text}\') and ${my_all_value}]`; } let result = myDispose_count_evaluate(xpath); if (result) { let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } } } else { /*if (attribute_value === "" || /\d/.test(attribute_value)) {*/ if (attribute_value === "" || (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 = `//${tag_name}[contains(@${attribute_name}, "${attribute_value}")]`; let parameter = `driver, By.XPATH, "${xpath}"`; myDispose_success(parameter); return parameter; } let xpath = ""; if (text === "") { xpath = `//${tag_name}[${my_all_value}]`; } else { xpath = `//${tag_name}[contains(text(),\'${text}\') and ${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 !== "") { 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 { let textStr = `self.myWtClickEx(driver, By.XPATH, "${xpath}")`; console.log("# 不是1"); console.log(textStr); console.log(`結束 父${father_level} -------------------------`); if (father_level === 0) { child_ctl = `father, By.XPATH, ".${xpath}"`; child_ctl_tmp = `.${xpath}`; next_focusedElement = focusedElement; } let father = focusedElement.parentElement; if (father) { father_level++; myDispose_click(father); } } } return null; } function myDispose_success(parameter) { if (father_level === 0) { console.log(`self.myWtClickEx(${parameter})`); } else { console.log(`father = self.myWtFindElement(${parameter})`); console.log(`self.myWtClickEx(${child_ctl})`); } console.log(`結束 父${father_level} -------------------------`); } function myDispose_count_evaluate(xpath) { let elements = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (father_level === 0) { if (elements.snapshotLength === 1) { return true } else { return null } } else { if (elements.snapshotLength === 1) { let firstElement = elements.snapshotItem(0); let result = document.evaluate(child_ctl_tmp, firstElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (result.snapshotLength === 1) { return true } else { return null } } else { 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) { console.log(`${attribute_name}:"${attribute_value}" 在網頁中出現1次`); return true; } else { console.log(`${attribute_name}:"${attribute_value}" 在網頁中出現了多次`); return null; }; } function getElementfullXPath(element) { if (element && element.id) if (!element.id.match(/[0-9]/)) { return \'//*[@id="\' + element.id + \'"]\'; } if (element==null) return ""; var index = 0; var loacl_tagName = element.tagName; var sibling = element.previousSibling; var sibling_tagName = null; if (sibling) { sibling_tagName = sibling.tagName; } while (sibling && sibling.nodeType === 1 && loacl_tagName === sibling_tagName) { index++; sibling = sibling.previousSibling; if (sibling) { sibling_tagName = sibling.tagName; } else { sibling_tagName = null; } } parent = element.parentNode; if (parent) { var xpath = getElementfullXPath(parent); if (xpath === "undefined") { return ""; } else { if (index === 0) { xpath += "/" + element.tagName.toLowerCase(); } else { xpath += "/" + element.tagName.toLowerCase() + "[" + (index+1) + "]"; } return xpath; } } else { return ""; } } '; var input_textContent = ' let inputs = document.querySelectorAll(`input[type="text"]`); inputs.forEach(input => { input.addEventListener("input", function(event) { console.log(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; console.log(textStr); } }); }); '; /*iframe*/ let iframes = document.getElementsByTagName('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 = click_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(this.value); let parameter = myDispose_click(event.target); if (parameter !== null) { let textStr = `self.myWtSendKeysWebEx(${parameter}, "${this.value}")`; console.log(textStr); } }); }); } } /*非iframe*/ let script = document.createElement('script'); script.type = 'text/javascript'; script.textContent = click_textContent + input_textContent; document.head.appendChild(script);