Batch translator tester for testCases format (not yet Chrome/Safari-compatible)
This commit is contained in:
parent
942fe0f97f
commit
dcbea215d9
4 changed files with 483 additions and 0 deletions
|
@ -0,0 +1,51 @@
|
||||||
|
body {
|
||||||
|
font-family: Helvetica, sans;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-color: black;
|
||||||
|
border-width: 0 0 1px 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border-color: black;
|
||||||
|
border-width: 1px 1px 0 0;
|
||||||
|
border-style: solid;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-translator {
|
||||||
|
width: 500px;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-status {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-pending, .th-supported, .th-succeeded, .th-failed, .th-unknown {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-succeeded, .supported-yes {
|
||||||
|
background-color: #90ff90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-failed, .supported-no {
|
||||||
|
background-color: #ff9090;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-unknown {
|
||||||
|
background-color: #FFB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-untested {
|
||||||
|
background-color: #ececec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-pending {
|
||||||
|
background-color: #9FF;
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2011 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<script type="text/javascript" src="testTranslators.js"></script>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="testTranslators.css" />
|
||||||
|
<title>Zotero Translator Tester</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
228
chrome/content/zotero/tools/testTranslators/testTranslators.js
Normal file
228
chrome/content/zotero/tools/testTranslators/testTranslators.js
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2011 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
const CHROME_SAFARI_SCRIPTS = [
|
||||||
|
"zotero.js",
|
||||||
|
"zotero/date.js",
|
||||||
|
"zotero/debug.js",
|
||||||
|
"zotero/inject/translator.js",
|
||||||
|
"zotero/openurl.js",
|
||||||
|
"zotero/translate.js",
|
||||||
|
"zotero/utilities.js",
|
||||||
|
"zotero/messages.js",
|
||||||
|
"messaging_inject.js",
|
||||||
|
"translatorTests.js"
|
||||||
|
];
|
||||||
|
|
||||||
|
const TRANSLATOR_TYPES = ["Web", "Import", "Export", "Search"];
|
||||||
|
const TABLE_COLUMNS = ["Translator", "Supported", "Status", "Pending", "Succeeded", "Failed", "Unknown"];
|
||||||
|
var translatorTables = {};
|
||||||
|
var translatorTestViewsToRun = {};
|
||||||
|
var Zotero;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a set of tests for a specific translator and type
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var TranslatorTestView = function(translator, type) {
|
||||||
|
this._translator = translator;
|
||||||
|
|
||||||
|
var row = document.createElement("tr");
|
||||||
|
|
||||||
|
// Translator
|
||||||
|
this._label = document.createElement("td");
|
||||||
|
this._label.appendChild(document.createTextNode(translator.label));
|
||||||
|
row.appendChild(this._label);
|
||||||
|
|
||||||
|
// Supported
|
||||||
|
this._supported = document.createElement("td");
|
||||||
|
var isSupported = translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER;
|
||||||
|
this._supported.appendChild(document.createTextNode(isSupported ? "Yes" : "No"));
|
||||||
|
this._supported.className = isSupported ? "supported-yes" : "supported-no";
|
||||||
|
row.appendChild(this._supported);
|
||||||
|
|
||||||
|
// Status
|
||||||
|
this._status = document.createElement("td");
|
||||||
|
row.appendChild(this._status);
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
this._pending = document.createElement("td");
|
||||||
|
row.appendChild(this._pending);
|
||||||
|
|
||||||
|
// Succeeded
|
||||||
|
this._succeeded = document.createElement("td");
|
||||||
|
row.appendChild(this._succeeded);
|
||||||
|
|
||||||
|
// Failed
|
||||||
|
this._failed = document.createElement("td");
|
||||||
|
row.appendChild(this._failed);
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
this._unknown = document.createElement("td");
|
||||||
|
row.appendChild(this._unknown);
|
||||||
|
|
||||||
|
// append to table
|
||||||
|
translatorTables[type].appendChild(row);
|
||||||
|
|
||||||
|
// create translator tester and update status based on what it knows
|
||||||
|
this._translatorTester = new Zotero_TranslatorTester(translator, type);
|
||||||
|
this.updateStatus();
|
||||||
|
this.hasTests = !!this._translatorTester.tests.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the displayed status of a translator
|
||||||
|
*/
|
||||||
|
TranslatorTestView.prototype.updateStatus = function() {
|
||||||
|
if(this._translatorTester.tests.length) {
|
||||||
|
if(this._translatorTester.pending.length) {
|
||||||
|
this._status.className = "status-pending";
|
||||||
|
this._status.textContent = "Pending";
|
||||||
|
} else if(this._translatorTester.failed.length) {
|
||||||
|
this._status.className = "status-failed";
|
||||||
|
this._status.textContent = "Failed";
|
||||||
|
} else if(this._translatorTester.unknown.length) {
|
||||||
|
this._status.className = "status-unknown";
|
||||||
|
this._status.textContent = "Unknown";
|
||||||
|
} else {
|
||||||
|
this._status.className = "status-succeeded";
|
||||||
|
this._status.textContent = "Succeeded";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._status.className = "status-untested";
|
||||||
|
this._status.textContent = "Untested";
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pending.textContent = this._translatorTester.pending.length;
|
||||||
|
this._succeeded.textContent = this._translatorTester.succeeded.length;
|
||||||
|
this._failed.textContent = this._translatorTester.failed.length;
|
||||||
|
this._unknown.textContent = this._translatorTester.unknown.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs test for this translator
|
||||||
|
*/
|
||||||
|
TranslatorTestView.prototype.runTests = function(doneCallback) {
|
||||||
|
var me = this;
|
||||||
|
if(Zotero.isFx) {
|
||||||
|
// yay, no message passing
|
||||||
|
var i = 1;
|
||||||
|
this._translatorTester.runTests(function(status, message) {
|
||||||
|
me.updateStatus();
|
||||||
|
if(me._translatorTester.pending.length === 0) {
|
||||||
|
doneCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when loaded
|
||||||
|
*/
|
||||||
|
function load(event) {
|
||||||
|
if(window.chrome || window.safari) {
|
||||||
|
// load scripts
|
||||||
|
for(var i in CHROME_SAFARI_SCRIPTS) {
|
||||||
|
var script = document.createElement("script");
|
||||||
|
script.setAttribute("type", "text/javascript");
|
||||||
|
script.setAttribute("src", CHROME_SAFARI_SCRIPTS[i]);
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
Zotero.initInject();
|
||||||
|
} else {
|
||||||
|
// load scripts
|
||||||
|
Zotero = Components.classes["@zotero.org/Zotero;1"]
|
||||||
|
.getService(Components.interfaces.nsISupports)
|
||||||
|
.wrappedJSObject;
|
||||||
|
Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
||||||
|
.getService(Components.interfaces.mozIJSSubScriptLoader)
|
||||||
|
.loadSubScript("chrome://zotero/content/tools/testTranslators/translatorTester.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i in TRANSLATOR_TYPES) {
|
||||||
|
var displayType = TRANSLATOR_TYPES[i];
|
||||||
|
var translatorType = displayType.toLowerCase();
|
||||||
|
|
||||||
|
// create header
|
||||||
|
var h1 = document.createElement("h1");
|
||||||
|
h1.appendChild(document.createTextNode(displayType+" Translators"));
|
||||||
|
document.body.appendChild(h1);
|
||||||
|
|
||||||
|
// create table
|
||||||
|
var translatorTable = document.createElement("table");
|
||||||
|
translatorTables[translatorType] = translatorTable;
|
||||||
|
|
||||||
|
// add headings to table
|
||||||
|
var headings = document.createElement("tr");
|
||||||
|
for(var j in TABLE_COLUMNS) {
|
||||||
|
var th = document.createElement("th");
|
||||||
|
th.className = "th-"+TABLE_COLUMNS[j].toLowerCase();
|
||||||
|
th.appendChild(document.createTextNode(TABLE_COLUMNS[j]));
|
||||||
|
headings.appendChild(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append to document
|
||||||
|
translatorTable.appendChild(headings);
|
||||||
|
document.body.appendChild(translatorTable);
|
||||||
|
|
||||||
|
// get translators, with code for unsupported translators
|
||||||
|
Zotero.Translators.getAllForType(translatorType, new function() {
|
||||||
|
var type = translatorType;
|
||||||
|
return function(translators) {
|
||||||
|
haveTranslators(translators, type);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after translators are returned from main script
|
||||||
|
*/
|
||||||
|
function haveTranslators(translators, type) {
|
||||||
|
translatorTestViewsToRun[type] = [];
|
||||||
|
|
||||||
|
for(var i in translators) {
|
||||||
|
var translatorTestView = new TranslatorTestView(translators[i], type);
|
||||||
|
if(translatorTestView.hasTests) {
|
||||||
|
translatorTestViewsToRun[type].push(translatorTestView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runTranslatorTests(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs translator tests recursively, after translatorTestViews has been populated
|
||||||
|
*/
|
||||||
|
function runTranslatorTests(type) {
|
||||||
|
if(translatorTestViewsToRun[type].length) {
|
||||||
|
var translatorTestView = translatorTestViewsToRun[type].shift();
|
||||||
|
translatorTestView.runTests(function() { runTranslatorTests(type) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("load", load, false);
|
169
chrome/content/zotero/tools/testTranslators/translatorTester.js
Normal file
169
chrome/content/zotero/tools/testTranslators/translatorTester.js
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2009 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Zotero_TranslatorTester_IGNORE_FIELDS = ["complete", "accessDate", "checkFields"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tool to run unit tests for a given translator
|
||||||
|
*
|
||||||
|
* @property {Array} tests All tests for this translator
|
||||||
|
* @property {Array} pending All tests for this translator
|
||||||
|
* @property {Array} succeeded All tests for this translator
|
||||||
|
* @property {Array} failed All tests for this translator
|
||||||
|
* @property {Array} unknown All tests for this translator
|
||||||
|
* @constructor
|
||||||
|
* @param {Zotero.Translator[]} translator The translator for which to run tests
|
||||||
|
* @param {String} type The type of tests to run (web, import, export, or search)
|
||||||
|
*/
|
||||||
|
var Zotero_TranslatorTester = function(translator, type, debug) {
|
||||||
|
this._type = type;
|
||||||
|
this._translator = translator;
|
||||||
|
this._debug = (debug ? debug : function(a, b) { Zotero.debug(a, b) });
|
||||||
|
|
||||||
|
this.tests = [];
|
||||||
|
this.pending = [];
|
||||||
|
this.succeeded = [];
|
||||||
|
this.failed = [];
|
||||||
|
this.unknown = [];
|
||||||
|
|
||||||
|
var code = translator.code;
|
||||||
|
var testStart = code.indexOf("/** BEGIN TEST CASES **/");
|
||||||
|
var testEnd = code.indexOf("/** END TEST CASES **/");
|
||||||
|
if (testStart !== -1 && testEnd !== -1) {
|
||||||
|
var test = code.substring(testStart + 24, testEnd);
|
||||||
|
test = test.replace(/var testCases = /,'');
|
||||||
|
// The JSON parser doesn't like final semicolons
|
||||||
|
if (test.lastIndexOf(';') == (test.length-1)) {
|
||||||
|
test = test.slice(0,-1);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var testObject = JSON.parse(test);
|
||||||
|
} catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i in testObject) {
|
||||||
|
if(testObject[i].type === type) {
|
||||||
|
this.tests.push(testObject[i]);
|
||||||
|
this.pending.push(testObject[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes tests for this translator
|
||||||
|
* @param {Function} testDoneCallback A callback to be executed each time a test is complete
|
||||||
|
*/
|
||||||
|
Zotero_TranslatorTester.prototype.runTests = function(testDoneCallback, recursiveRun) {
|
||||||
|
if(!recursiveRun) {
|
||||||
|
this._debug("TranslatorTester: Running "+this.pending.length+" tests for "+this._translator.label);
|
||||||
|
}
|
||||||
|
if(!this.pending.length) {
|
||||||
|
// always call testDoneCallback once if there are no tests
|
||||||
|
if(!recursiveRun) testDoneCallback("unknown", "No tests present");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var test = this.pending.shift();
|
||||||
|
var testNumber = this.tests.length-this.pending.length;
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
var callback = function(status, message) {
|
||||||
|
me._debug("TranslatorTester: "+me._translator.label+" Test "+testNumber+": "+status+" ("+message+")");
|
||||||
|
me[status].push(test);
|
||||||
|
if(testDoneCallback) testDoneCallback(status, message);
|
||||||
|
me.runTests(testDoneCallback, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
Zotero.HTTP.processDocuments(test.url,
|
||||||
|
function(doc) {
|
||||||
|
me.runTest(test, doc, callback);
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
function(e) {
|
||||||
|
callback("failed", "Translation failed to initialize: "+e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a test for a translator, given the document to test upon
|
||||||
|
* @param {Object} test Test to execute
|
||||||
|
* @param {Document} doc DOM document to test against
|
||||||
|
* @param {Function} testDoneCallback A callback to be executed when test is complete
|
||||||
|
*/
|
||||||
|
Zotero_TranslatorTester.prototype.runTest = function(test, doc, testDoneCallback) {
|
||||||
|
this._debug(test);
|
||||||
|
var me = this;
|
||||||
|
var translate = Zotero.Translate.newInstance(this._type);
|
||||||
|
translate.setDocument(doc);
|
||||||
|
translate.setTranslator(this._translator);
|
||||||
|
translate.setHandler("done", function(obj, returnValue) { me._checkResult(test, obj, returnValue, testDoneCallback) });
|
||||||
|
translate.translate(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the results of translation match what is expected by the test
|
||||||
|
* @param {Object} test Test that was executed
|
||||||
|
* @param {Zotero.Translate} translate The Zotero.Translate instance
|
||||||
|
* @param {Boolean} returnValue Whether translation completed successfully
|
||||||
|
* @param {Function} testDoneCallback A callback to be executed when test is complete
|
||||||
|
*/
|
||||||
|
Zotero_TranslatorTester.prototype._checkResult = function(test, translate, returnValue, testDoneCallback) {
|
||||||
|
if(!returnValue) {
|
||||||
|
testDoneCallback("failed", "Translation failed; examine debug output for errors");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!translate.newItems.length) {
|
||||||
|
testDoneCallback("failed", "Translation failed; no items returned");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(translate.newItems.length !== test.items.length) {
|
||||||
|
testDoneCallback("unknown", "Expected "+test.items.length+" items; got "+translate.newItems.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i in test.items) {
|
||||||
|
var testItem = test.items[i];
|
||||||
|
var translatedItem = translate.newItems[i];
|
||||||
|
|
||||||
|
for(var j in Zotero_TranslatorTester_IGNORE_FIELDS) {
|
||||||
|
delete testItem[Zotero_TranslatorTester_IGNORE_FIELDS[j]];
|
||||||
|
delete translatedItem[Zotero_TranslatorTester_IGNORE_FIELDS[j]];
|
||||||
|
}
|
||||||
|
|
||||||
|
var testItemJSON = JSON.stringify(testItem);
|
||||||
|
var translatedItemJSON = JSON.stringify(translatedItem);
|
||||||
|
if(testItemJSON != translatedItemJSON) {
|
||||||
|
testDoneCallback("unknown", "Item "+i+" does not match");
|
||||||
|
this._debug("TranslatorTester: Mismatch between "+testItemJSON+" and "+translatedItemJSON);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testDoneCallback("succeeded", "Test succeeded");
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue