Unit testing infrastructure

Implements the beginnings of unit testing infrastructure using
mocha/chai. The unit tests can be run locally using test/runtests.sh,
although this will need tweaks to run on Windows. They should also run
on commit using Travis-CI.

The unit tests themselves live in test/tests. The index.js file
specifies separate test sets, which can be run individually by calling
test/runtests.sh <testsets>. Right now there is only a single unit
test, but hopefully we'll have more soon...
This commit is contained in:
Simon Kornblith 2015-03-07 12:18:27 -05:00
parent 5285fadded
commit 1c32db68da
12 changed files with 289 additions and 0 deletions

6
.gitmodules vendored
View file

@ -7,3 +7,9 @@
[submodule "styles"]
path = styles
url = git://github.com/zotero/bundled-styles.git
[submodule "test/resource/chai"]
path = test/resource/chai
url = https://github.com/chaijs/chai.git
[submodule "test/resource/mocha"]
path = test/resource/mocha
url = https://github.com/mochajs/mocha.git

16
.travis.yml Normal file
View file

@ -0,0 +1,16 @@
language: cpp
compiler:
- gcc
env:
matrix:
- FIREFOXVERSION="36.0.1"
- FIREFOXVERSION="31.5.0esr"
notifications:
email: false
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- wget http://ftp.mozilla.org/pub/firefox/releases/${FIREFOXVERSION}/linux-x86_64/en-US/firefox-${FIREFOXVERSION}.tar.bz2
- tar -xjf firefox-${FIREFOXVERSION}.tar.bz2
script:
- test/runtests.sh -x firefox/firefox

7
test/chrome.manifest Normal file
View file

@ -0,0 +1,7 @@
content zotero-unit content/
resource zotero-unit resource/
resource zotero-unit-tests tests/
component {b8570031-be5e-46e8-9785-38cd50a5d911} components/zotero-unit.js
contract @mozilla.org/commandlinehandler/general-startup;1?type=zotero-unit {b8570031-be5e-46e8-9785-38cd50a5d911}
category command-line-handler m-zotero-unit @mozilla.org/commandlinehandler/general-startup;1?type=zotero-unit

View file

@ -0,0 +1,51 @@
"use strict";
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2012 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 *****
*/
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function ZoteroUnit() {
this.wrappedJSObject = this;
}
ZoteroUnit.prototype = {
/* nsICommandLineHandler */
handle:function(cmdLine) {
this.tests = cmdLine.handleFlagWithParam("test", false);
},
dump:function(x) {
dump(x);
},
contractID: "@mozilla.org/commandlinehandler/general-startup;1?type=zotero-unit",
classDescription: "Zotero Unit Command Line Handler",
classID: Components.ID("{b8570031-be5e-46e8-9785-38cd50a5d911}"),
service: true,
_xpcom_categories: [{category:"command-line-handler", entry:"m-zotero-unit"}],
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsICommandLineHandler,
Components.interfaces.nsISupports])
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([ZoteroUnit]);

View file

@ -0,0 +1,14 @@
<html>
<head>
<meta charset="utf-8"></meta>
<title>Zotero Unit Tests</title>
</head>
<body>
<script src="jquery.js"></script>
<script src="chrome://zotero/content/include.js"></script>
<script src="resource://zotero-unit/chai/chai.js"></script>
<script src="resource://zotero-unit/mocha/mocha.js"></script>
<script src="resource://zotero-unit-tests/index.js"></script>
<script src="runtests.js" version="application/javascript;version=1.8"></script>
</body>
</html>

88
test/content/runtests.js Normal file
View file

@ -0,0 +1,88 @@
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm")
var ZoteroUnit = Components.classes["@mozilla.org/commandlinehandler/general-startup;1?type=zotero-unit"].
getService(Components.interfaces.nsISupports).
wrappedJSObject;
var dump = ZoteroUnit.dump;
function quit(failed) {
// Quit with exit status
if(!failed) {
OS.File.writeAtomic(FileUtils.getFile("ProfD", ["success"]).path, Uint8Array(0));
}
Components.classes['@mozilla.org/toolkit/app-startup;1'].
getService(Components.interfaces.nsIAppStartup).
quit(Components.interfaces.nsIAppStartup.eForceQuit);
}
function Reporter(runner) {
var indents = 0, passed = 0, failed = 0;
function indent() {
return Array(indents).join(' ');
}
runner.on('start', function(){});
runner.on('suite', function(suite){
++indents;
dump(indent()+suite.title+"\n");
});
runner.on('suite end', function(suite){
--indents;
if (1 == indents) dump("\n");
});
runner.on('pending', function(test){
dump(indent()+"pending -"+test.title);
});
runner.on('pass', function(test){
passed++;
var msg = "\r"+indent()+Mocha.reporters.Base.symbols.ok+" "+test.title;
if ('fast' != test.speed) {
msg += " ("+Math.round(test.duration)+" ms)";
}
dump(msg+"\n");
});
runner.on('fail', function(test, err){
failed++;
dump("\r"+indent()+Mocha.reporters.Base.symbols.err+" "+test.title+"\n");
});
runner.on('end', function() {
dump(passed+"/"+(passed+failed)+" tests passed.\n");
quit(failed != 0);
});
}
// Setup Mocha
mocha.setup({ui:"bdd", reporter:Reporter});
var assert = chai.assert,
expect = chai.expect;
// Set up tests to run
if(ZoteroUnit.tests) {
document.body.appendChild(document.createTextNode("Running tests..."));
var torun = ZoteroUnit.tests == "all" ? Object.keys(TESTS) : ZoteroUnit.tests.split(",");
for(var key of torun) {
if(!TESTS[key]) {
dump("Invalid test set "+torun+"\n");
quit(true);
}
for(var test of TESTS[key]) {
var el = document.createElement("script");
el.type = "application/javascript;version=1.8";
el.src = "resource://zotero-unit-tests/"+test;
document.body.appendChild(el);
}
}
}
window.onload = function() {
mocha.run();
};

26
test/install.rdf Normal file
View file

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>zotero-unit@zotero.org</em:id>
<em:name>Zotero Unit Tests</em:name>
<em:version>1.0</em:version>
<em:creator>Center for History and New Media</em:creator>
<em:developer>Simon Kornblith</em:developer>
<em:homepageURL>http://www.zotero.org</em:homepageURL>
<em:iconURL>chrome://zotero/skin/zotero-new-z-48px.png</em:iconURL>
<em:type>2</em:type> <!-- type=extension -->
<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>31.0</em:minVersion>
<em:maxVersion>38.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

1
test/resource/chai Submodule

@ -0,0 +1 @@
Subproject commit d7cafca0232756f767275bb00e66930a7823b027

1
test/resource/mocha Submodule

@ -0,0 +1 @@
Subproject commit 65fc80ecd96ca2159a792aff089bbc273d4bd86d

56
test/runtests.sh Executable file
View file

@ -0,0 +1,56 @@
#!/bin/bash
CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ "`uname`" == "Darwin" ]; then
FX_EXECUTABLE="/Applications/Firefox.app/Contents/MacOS/firefox"
else
FX_EXECUTABLE="firefox"
fi
function usage {
cat >&2 <<DONE
Usage: $0 [-x FX_EXECUTABLE] [TESTS...]
Options
-x FX_EXECUTABLE path to Firefox executable (default: $FX_EXECUTABLE)
TESTS set of tests to run (default: all)
DONE
exit 1
}
while getopts "x:" opt; do
case $opt in
x)
FX_EXECUTABLE="$OPTARG"
;;
*)
usage
;;
esac
shift $((OPTIND-1)); OPTIND=1
done
if [ -z $1 ]; then
TESTS="all"
else
ARGS=("${@:1}")
function join { local IFS="$1"; shift; echo "$*"; }
TESTS="$(join , "${ARGS[@]}")"
fi
# Set up profile directory
PROFILE="`mktemp -d 2>/dev/null || mktemp -d -t 'zotero-unit'`"
mkdir "$PROFILE/extensions"
echo "$CWD" > "$PROFILE/extensions/zotero-unit@zotero.org"
echo "`dirname "$CWD"`" > "$PROFILE/extensions/zotero@chnm.gmu.edu"
echo 'user_pref("extensions.autoDisableScopes", 0);' > "$PROFILE/prefs.js"
MOZ_NO_REMOTE=1 NO_EM_RESTART=1 "$FX_EXECUTABLE" -profile "$PROFILE" \
-chrome chrome://zotero-unit/content/runtests.html -test "$TESTS"
# Check for success
test -e "$PROFILE/success"
STATUS=$?
# Clean up
rm -rf "$PROFILE"
exit $STATUS

3
test/tests/index.js Normal file
View file

@ -0,0 +1,3 @@
var TESTS = {
"utilities":["utilities.js"]
};

20
test/tests/utilities.js Normal file
View file

@ -0,0 +1,20 @@
describe("Zotero.Utilities", function() {
describe("cleanAuthor", function() {
it('should parse author names', function() {
for(let useComma of [false, true]) {
for(let first_expected of [["First", "First"],
["First Middle", "First Middle"],
["F. R. S.", "F. R. S."],
["F.R.S.", "F. R. S."],
["F R S", "F. R. S."],
["FRS", "F. R. S."]]) {
let [first, expected] = first_expected;
let str = useComma ? "Last, "+first : first+" Last";
let author = Zotero.Utilities.cleanAuthor(str, "author", useComma);
assert.equal(author.firstName, expected);
assert.equal(author.lastName, "Last");
}
}
});
});
});