Selenium自動化測試有諸多不穩定因素,使用起來需要看大量API文檔,下載對應語言的驅動包裹。遂想直接用Chrome Extension取而代之。
如何用Chrome Extension驅動網頁UI測試呢?首先我們從第一個Extension說起。
一個Extension我們放在一個文件夾裏,第一個Extension有三個文件:icon.png, background.js, manifest.json
從網上有很多這樣的代碼,就是當特定瀏覽網頁時,在地址欄右側顯示出extension的圖標icon.png。
// background.js
function match(url){
var host = "null";
if(typeof url == "undefined" || null == url)
url = window.location.href;
var regex = /.*\:\/\/([^\/]*).*/;
var match = url.match(regex);
if(typeof match != "undefined" && null != match)
host = match[1];
return host;
}
function entry(tabId, changeInfo, tab) {
// 在URL是localhost時顯示extension圖標
if(match(tab.url).toLowerCase()=="localhost"){
chrome.pageAction.show(tabId);
}
};
chrome.tabs.onUpdated.addListener(entry);
// manifest.json
{
"manifest_version": 2,
"name": "Chrome Extension: Test",
"version": "0.0.1",
"background": { "scripts": ["background.js"] },
"permissions": ["tabs"],
"page_action": {
"default_icon": {
"19": "icon.png"
},
"default_title": "test extension"
}
}
接着打開Chrome,在地址欄輸入chrome://extensions/,勾選Extension標題右側“開發者模式”(developer mode)就可以看見按鈕去選文件夾添加自己的extension了。
載入extension後,在新tab中輸入測試頁面,如http://localhost/index.html,那麼地址欄的右側便會出現icon.png的圖標。
那個圖標暫時就當個裝飾,標明我們想要測試的頁面被handle到。
下面來說一說怎麼得到當前訪問的頁面的信息。這就要用到extension的content script特性了。在extension開發文件夾裏增加一個js文件content.js:
// content.js
var links = document.getElementsByTagName('a');
var list = [];
var n,i,t;
n = links.length;
for(i=0;i<n;i++) {
t = links[i].href;
if (!t) continue;
if (t.toLowerCase().indexOf('javascript:') >= 0) continue;
list.push(t);
t = null;
}
console.log(list);
然後在manifest.json裏添加content script的設置:
// manifest.json
... ...
"background": { "scripts": ["background.js"] },
// 添加content script的支持
"content_scripts":[
{"matches":["http://localhost/*"],"js":["content.js"]}
],
"permissions": ["tabs"],
... ...
把自己的extension在chrome://extensions/裏重新加載(Reload)一下,再刷新一下測試頁面;這時進入控制檯,如果頁面裏有超鏈接,那麼控制檯會把它們鏈接到的地址全部打印出來。當然,控制檯輸出結果是一種形式,輸出到文件也可以,我們可以把最開始那個圖標用起來,點擊它看結果。
實現點擊圖標看結果需要知道background和content script如何通信。我們先談一談如何將content script裏得到的list讓background得到,並在圖標點擊後顯示出來。
content script中想把list交給background,需要通過消息機制實現通信。修改content.js和background.js:
// content.js
... ...
console.log(list);
chrome.runtime.sendMessage({
type: 'localtest-links',
links: list
});
// background.js
... ...
chrome.tabs.onUpdated.addListener(entry);
var pageData = {};
chrome.runtime.onMessage.addListener(function(message, sender, response){
if (message.type !== 'localtest-links') return;
pageData.links = message.links;
});
點擊圖標顯示內容其實就是配置一個popup的頁面,需要添加兩個文件:popup.html和popup.js,並修改manifest.json。
<!-- popup.html -->
<html>
<body>
<div id="result">(No Result)</div>
<script type="text/javascript" src="popup.js"></script>
</body>
</html>
// popup.js
document.addEventListener('DOMContentLoaded', function () {
var data = chrome.extension.getBackgroundPage().pageData;
var dom = document.getElementById('result');
var html = '';
var n, i;
n = data.links.length;
for(i=0;i<n;i++) {
html += '<div>' + data.links[i] + '</div>';
}
dom.innerHTML = html;
html = null;
dom = null;
data = null;
});
// manifest.json
... ...
"default_title": "test extension",
"default_popup": "popup.html"
}
... ...
這樣訪問localhost的頁面時,所有超鏈接信息都會被收集到,並在點擊extension圖標後顯示在彈出的小窗口中。
上面整套流程基本就可以運行一些簡單的測試了,測試人員可以使用腳本打開chrome,然後讓extension自動執行測試,最後extension圖標可以讓測試人員看到測試結果。
下面就讓我們想一想用extension測試時,把控制面板放入popup.html中;這裏我們添加一個按鈕,點擊這個按鈕後,會觸發被測試頁面中的第一個button的click單擊事件。
先腦補一下事件的觸發:
function trigger(element, eventType) {
var event = document.createEvent('HTMLEvents');
event.initEvent(eventType, true, true);
element.dispatchEvent(event);
event = null;
}
一個控制按鈕要加到popup.html中,接着就是處理從popup中發送信息告知content script我要觸發button的click事件:
<!-- popup.html -->
... ...
<div id="result">(None)</div>
<button id="fire">Fire</button>
<script src="popup.js"></script>
... ...
// popup.js
... ...
dom.innerHTML = html;
dom = document.getElementById('fire');
dom.addEventListener('click', function () {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(
tabs[0].id,
{type: 'localtest-fire'},
function(response) {});
});
});
html = null;
... ...
// content.js
... ...
chrome.extension.onMessage.addListener(function(message, sender, response) {
if(message.type!=='localtest-fire') return;
var buttons = document.getElementsByTagName('button');
if(buttons.length > 0) {
// trigger就是剛纔腦補的那部分 :P
trigger(buttons[0], 'click');
}
buttons = null;
});
爲了實驗效果,我們可以在本地服務器上放一個test.html文件:
<html>
<body>
<a href="http://www.baidu.com"">Hello</a><br/>
<a href="javascript:alert('hi');">World</a><br/>
<a href="test.html">self</a><br/>
<button οnclick="javascript:alert('trigger')">From Popup</button>
</body>
</html>
重新加載extension後,當我們訪問http://localhost/test.html時,點擊地址欄右邊extension的圖標,點擊Fire按鈕,這樣就會有alert彈出,上面寫着"trigger"!
但是,自動測試的時候出現alert,測試就暫停了,這個是令人抓狂的,我們得屏蔽掉alert。下面就說一說content script向當前頁面注入代碼,目標是幹掉alert。
我們知道window.alert就是alert,執行它會彈出一個消息框。只要能重載這個函數,讓它把消息輸出到console上就好了。需要修改content.js:
// content.js
... ...
var script = document.createElement('script');
script.type = 'text/javascript';
// 將alert替換,直接將msg輸出到console
script.textContent = 'window.nativeAlert=window.alert;window.alert=function(msg){console.log(msg);};';
// 將代碼注入到當前頁面中
(document.head || document.documentElement).appendChild(script);
// 可以讓代碼運行完後再把script從頁面移除,不過這個就不很重要了
script = null;
重載extension,訪問test.html時,直接單擊“From Popup”按鈕就可以發現消息已經輸出到console了,點擊popup裏的“fire”按鈕效果同樣。
當然,還可以用這種注入的方式把測試腳本加載上運行,然後返回執行結果,這樣就可以做成不錯的網頁UI自動化測試工具了。
對於HTTPS,有時候我們會遇到彈出輸入驗證對話框,那我們就可以啓用extension的webRequest功能,在Header中加入Authentication的信息就可以避開這個對話框的出現。
好了,現在網頁UI自動化測試基本可以跑自己的extension了,寫寫代碼還能把結果輸出成文本,夠用了~就不用對着selium各種神傷了。
J.Y.Liu
2014.09.15