gabrielg-xultestrunner 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/LICENSE +21 -0
- data/README +6 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/bin/xultest +12 -0
- data/xulapp/application.ini +9 -0
- data/xulapp/chrome/chrome.manifest +2 -0
- data/xulapp/chrome/content/lib/shortcuts.js +15 -0
- data/xulapp/chrome/content/lib/xultestcase.js +126 -0
- data/xulapp/chrome/content/lib/xultestrunner.js +114 -0
- data/xulapp/chrome/content/vendor/prototype/LICENSE +16 -0
- data/xulapp/chrome/content/vendor/prototype/prototype.js +4874 -0
- data/xulapp/chrome/content/vendor/scriptaculous/LICENSE +20 -0
- data/xulapp/chrome/content/vendor/scriptaculous/test.css +90 -0
- data/xulapp/chrome/content/vendor/scriptaculous/testharness.html +31 -0
- data/xulapp/chrome/content/vendor/scriptaculous/unittest.js +566 -0
- data/xulapp/chrome/content/xul/boot.xul +20 -0
- data/xulapp/components/bootstrap.js +92 -0
- data/xulapp/defaults/preferences/prefs.js +14 -0
- data/xultestrunner.gemspec +59 -0
- metadata +75 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) <2009> <Gabriel Gironda>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
require 'uuidtools'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
Jeweler::Tasks.new do |s|
|
7
|
+
s.name = "xultestrunner"
|
8
|
+
s.executables = "xultest"
|
9
|
+
s.summary = "XUL based test runner for running your JS unit tests."
|
10
|
+
s.email = "contact@gironda.org"
|
11
|
+
s.homepage = "http://github.com/gabrielg/xultestrunner"
|
12
|
+
s.description = "XUL based test runner for running your JS unit tests."
|
13
|
+
s.authors = ["Gabriel Gironda"]
|
14
|
+
end
|
15
|
+
|
16
|
+
application_ini_path = (Pathname(__FILE__).parent + "xulapp/application.ini").expand_path
|
17
|
+
|
18
|
+
desc "Writes out a random UUID for the Build ID when we release to the XUL application's application.ini"
|
19
|
+
task :write_xul_build_id do
|
20
|
+
build_id = UUIDTools::UUID.random_create.to_s
|
21
|
+
ini_contents = application_ini_path.read
|
22
|
+
application_ini_path.open('w') do |f|
|
23
|
+
f << ini_contents.sub(/^BuildID=.*$/, "BuildID=#{build_id}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Writes out the gem version to the XUL application's application.ini"
|
28
|
+
task :write_xul_version do
|
29
|
+
version = (Pathname(__FILE__).parent + "VERSION").read.chomp
|
30
|
+
ini_contents = application_ini_path.read
|
31
|
+
application_ini_path.open('w') do |f|
|
32
|
+
f << ini_contents.sub(/^Version=.*$/, "Version=#{version}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Commits the application ini before a release"
|
37
|
+
task :commit_application_ini do
|
38
|
+
system("git", "add", application_ini_path.to_s)
|
39
|
+
system("git", "commit", "-m", "Bumping application.ini", application_ini_path.to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
task :release => [:write_xul_build_id, :write_xul_version, :commit_application_ini]
|
43
|
+
|
44
|
+
rescue LoadError
|
45
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
46
|
+
end
|
47
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/xultest
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
firefox_location = [`which firefox-bin`.chomp, `which firefox`.chomp].detect do |loc|
|
5
|
+
!loc.length.zero?
|
6
|
+
end
|
7
|
+
|
8
|
+
raise "Could not find `firefox-bin` or `firefox` in your path. Please rectify this and try again." unless firefox_location
|
9
|
+
|
10
|
+
xulapp_ini_path = (Pathname(__FILE__).parent.parent + "xulapp/application.ini").expand_path
|
11
|
+
|
12
|
+
exec(firefox_location, *["-app", xulapp_ini_path, *ARGV])
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Cc = Components.classes;
|
2
|
+
Ci = Components.interfaces;
|
3
|
+
Cr = Components.results;
|
4
|
+
Cu = Components.utils;
|
5
|
+
loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
|
6
|
+
load = function(fileURI, moduleScope) {
|
7
|
+
try {
|
8
|
+
loader.loadSubScript(fileURI, moduleScope);
|
9
|
+
} catch (e) {
|
10
|
+
throw({name: "LoadError", message: "Failed to load " + fileURI + ": " + e.message});
|
11
|
+
}
|
12
|
+
};
|
13
|
+
|
14
|
+
print = dump;
|
15
|
+
puts = function(str) { print(str + "\n"); };
|
@@ -0,0 +1,126 @@
|
|
1
|
+
var XULTestCase = Class.create({
|
2
|
+
initialize: function(testName, testDef) {
|
3
|
+
this.testRunner = new XULTestCase.TestRunner(testDef);
|
4
|
+
this.testRunner.name = testName;
|
5
|
+
},
|
6
|
+
|
7
|
+
go: function() {
|
8
|
+
this.testRunner.runTests();
|
9
|
+
}
|
10
|
+
|
11
|
+
});
|
12
|
+
|
13
|
+
Object.extend(XULTestCase, {
|
14
|
+
create: function(testName, testDefinitionFunc) {
|
15
|
+
var tests = {};
|
16
|
+
var setupCollector = function(givenSetup) { tests['setup'] = givenSetup; };
|
17
|
+
var teardownCollector = function(givenTeardown) { tests['teardown'] = givenTeardown; };
|
18
|
+
var testCollector = function(testName, testDef) { tests["test " + testName] = testDef; };
|
19
|
+
testDefinitionFunc(setupCollector, teardownCollector, testCollector);
|
20
|
+
var testCase = new XULTestCase(testName, tests);
|
21
|
+
this.__LAST_DEFINED_TESTCASE__ = testCase;
|
22
|
+
return testCase
|
23
|
+
},
|
24
|
+
|
25
|
+
loadFromFile: function(fileURI) {
|
26
|
+
try {
|
27
|
+
load(fileURI);
|
28
|
+
if (this.__LAST_DEFINED_TESTCASE__) {
|
29
|
+
return this.__LAST_DEFINED_TESTCASE__;
|
30
|
+
} else {
|
31
|
+
throw("wuh oh");
|
32
|
+
}
|
33
|
+
} catch (e) {
|
34
|
+
this._handleTestLoadError(fileURI, e);
|
35
|
+
} finally {
|
36
|
+
this.__LAST_DEFINED_TESTCASE__ = null;
|
37
|
+
}
|
38
|
+
},
|
39
|
+
|
40
|
+
_handleTestLoadError: function(e) {
|
41
|
+
puts("There was a problem loading the test case from '" + fileURI + "'");
|
42
|
+
}
|
43
|
+
|
44
|
+
});
|
45
|
+
|
46
|
+
XULTestCase.Logger = Class.create(Test.Unit.Logger, {
|
47
|
+
consoleOutputMapping: {failed: 'F', error: 'E', passed: '.'},
|
48
|
+
|
49
|
+
initialize: function($super, log) {
|
50
|
+
$super(log);
|
51
|
+
this.unpassedTests = [];
|
52
|
+
},
|
53
|
+
|
54
|
+
finish: function ($super, status, summary) {
|
55
|
+
$super(status, summary);
|
56
|
+
print(this.consoleOutputMapping[status]);
|
57
|
+
if (status != "passed") {
|
58
|
+
this.unpassedTests.push({name: this.testName, status: status, summary: summary});
|
59
|
+
}
|
60
|
+
}
|
61
|
+
});
|
62
|
+
|
63
|
+
XULTestCase.TestRunner = Class.create(Test.Unit.Runner, {
|
64
|
+
// FIXME - ugh. straight rip from unittest.js just so we can ditch the settimeout
|
65
|
+
// and instantiate our own logger.
|
66
|
+
|
67
|
+
initialize: function(testcases) {
|
68
|
+
this.options = Object.extend({
|
69
|
+
testLog: 'testlog'
|
70
|
+
}, arguments[1] || {});
|
71
|
+
this.options.resultsURL = this.parseResultsURLQueryParameter();
|
72
|
+
this.options.tests = this.parseTestsQueryParameter();
|
73
|
+
if (this.options.testLog) {
|
74
|
+
this.options.testLog = $(this.options.testLog) || null;
|
75
|
+
}
|
76
|
+
if(this.options.tests) {
|
77
|
+
this.tests = [];
|
78
|
+
for(var i = 0; i < this.options.tests.length; i++) {
|
79
|
+
if(/^test/.test(this.options.tests[i])) {
|
80
|
+
this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
|
81
|
+
}
|
82
|
+
}
|
83
|
+
} else {
|
84
|
+
if (this.options.test) {
|
85
|
+
this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
|
86
|
+
} else {
|
87
|
+
this.tests = [];
|
88
|
+
for(var testcase in testcases) {
|
89
|
+
if(/^test/.test(testcase)) {
|
90
|
+
this.tests.push(
|
91
|
+
new Test.Unit.Testcase(
|
92
|
+
this.options.context ? ' -> ' + this.options.titles[testcase] : testcase,
|
93
|
+
testcases[testcase], testcases["setup"], testcases["teardown"]
|
94
|
+
));
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
this.currentTest = 0;
|
100
|
+
this.logger = new XULTestCase.Logger(this.options.testLog);
|
101
|
+
},
|
102
|
+
|
103
|
+
postResults: function($super) {
|
104
|
+
$super();
|
105
|
+
this._finishUpTestCase();
|
106
|
+
},
|
107
|
+
|
108
|
+
_finishUpTestCase: function() {
|
109
|
+
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
110
|
+
var data = {name: this.name, unpassedTests: this.logger.unpassedTests,
|
111
|
+
summary: this._summaryData(), testLength: this.tests.length};
|
112
|
+
// Need to pass between observers as JSON to avoid crashes. Wee.
|
113
|
+
observerService.notifyObservers(null, "xultestrunner-testdone", Object.toJSON(data));
|
114
|
+
},
|
115
|
+
|
116
|
+
_summaryData: function() {
|
117
|
+
var assertions = 0, failures = 0, errors = 0;
|
118
|
+
$A(this.tests).each(function(test) {
|
119
|
+
assertions += test.assertions;
|
120
|
+
failures += test.failures;
|
121
|
+
errors += test.errors;
|
122
|
+
});
|
123
|
+
return {assertions: assertions, failures: failures, errors: errors};
|
124
|
+
}
|
125
|
+
|
126
|
+
});
|
@@ -0,0 +1,114 @@
|
|
1
|
+
var XULTestRunner = Class.create({
|
2
|
+
initialize: function(options) {
|
3
|
+
this.options = options;
|
4
|
+
this._testResults = [];
|
5
|
+
this.appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
6
|
+
},
|
7
|
+
|
8
|
+
handleExit: function (exitWithFailure) {
|
9
|
+
if (exitWithFailure) {
|
10
|
+
puts("Test Failures!");
|
11
|
+
// We kill ourselves this way because we need a non-zero exit status. This is really hacky.
|
12
|
+
// Really, really hacky.
|
13
|
+
var envFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
14
|
+
envFile.initWithPath("/usr/bin/env");
|
15
|
+
var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
16
|
+
process.init(envFile);
|
17
|
+
process.run(true, ["ruby", "-e", "Process.kill(9, Process.ppid)"], 3);
|
18
|
+
}
|
19
|
+
this.appStartup.quit(Ci.nsIAppStartup.eForceQuit);
|
20
|
+
},
|
21
|
+
|
22
|
+
|
23
|
+
go: function () {
|
24
|
+
this._registerObservers();
|
25
|
+
var testsToRun = $A(this.options.tests).collect(function(testFile) { return '"' + testFile.path + '"'; });
|
26
|
+
puts("Running tests...");
|
27
|
+
puts(testsToRun.join(" ") + "\n");
|
28
|
+
this._runNextTest();
|
29
|
+
},
|
30
|
+
|
31
|
+
observe: function(subject, topic, data) {
|
32
|
+
// Need to pass between observers as JSON to avoid crashes. Wee.
|
33
|
+
this._testResults.push(data.evalJSON());
|
34
|
+
this._runNextTest();
|
35
|
+
},
|
36
|
+
|
37
|
+
_runNextTest: function() {
|
38
|
+
var testFile = this.options.tests.shift();
|
39
|
+
if (testFile) {
|
40
|
+
this._runTest(testFile);
|
41
|
+
} else {
|
42
|
+
this._finishTestRun()
|
43
|
+
}
|
44
|
+
},
|
45
|
+
|
46
|
+
_runTest: function(testFile) {
|
47
|
+
var qs = Object.toQueryString({testFile: "file://" + encodeURI(testFile.path)});
|
48
|
+
var harnessWithPath = this.options.harnessURI + "?" + qs;
|
49
|
+
this.options.browser.loadURI(harnessWithPath);
|
50
|
+
},
|
51
|
+
|
52
|
+
_finishTestRun: function() {
|
53
|
+
print("\n\n");
|
54
|
+
var exitWithFailure = this._doSummary();
|
55
|
+
this.handleExit(exitWithFailure);
|
56
|
+
},
|
57
|
+
|
58
|
+
_doSummary: function() {
|
59
|
+
var testRunners = 0, testCases = 0, unpassedLength = 0, assertions = 0, failures = 0, errors = 0;
|
60
|
+
$A(this._testResults).each(function(testResult) {
|
61
|
+
$A(testResult.unpassedTests).each(function(unpassedTest) {
|
62
|
+
unpassedLength += 1;
|
63
|
+
this._showSummaryForTestResult(testResult.name, unpassedTest, unpassedLength);
|
64
|
+
}, this);
|
65
|
+
testRunners++;
|
66
|
+
testCases += testResult.testLength;
|
67
|
+
assertions += testResult.summary.assertions;
|
68
|
+
failures += testResult.summary.failures;
|
69
|
+
errors += testResult.summary.errors;
|
70
|
+
}, this);
|
71
|
+
|
72
|
+
var overallSummary = "\n\n#{runners} runners, #{cases} cases, #{unpassed} unpassed, #{assertions} assertions, #{failures} failures, #{errors} errors.\n";
|
73
|
+
puts(overallSummary.interpolate({runners: testRunners, cases: testCases,
|
74
|
+
unpassed: unpassedLength, assertions: assertions, failures: failures, errors: errors}));
|
75
|
+
return unpassedLength > 0;
|
76
|
+
},
|
77
|
+
|
78
|
+
_showSummaryForTestResult: function(testName, testResult, failureIndex) {
|
79
|
+
puts("#{index}) #{testName} - #{name}".interpolate({index: failureIndex, testName: testName, name: testResult.name}));
|
80
|
+
puts(" " + testResult.status.toUpperCase() + ":\n");
|
81
|
+
puts(testResult.summary.replace(/^/mg, " ") + "\n\n");
|
82
|
+
},
|
83
|
+
|
84
|
+
_registerObservers: function() {
|
85
|
+
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
86
|
+
observerService.addObserver(this, "xultestrunner-testdone", false);
|
87
|
+
}
|
88
|
+
});
|
89
|
+
|
90
|
+
Object.extend(XULTestRunner, {
|
91
|
+
testFileMatcher: /_test\.js$/,
|
92
|
+
initializeFromCmdArgs: function(cmdArgs, options) {
|
93
|
+
if (cmdArgs.testDir) {
|
94
|
+
var tests = this._findTestsFromDir(cmdArgs.testDir);
|
95
|
+
} else if (cmdArgs.testFile) {
|
96
|
+
var tests = [cmdArgs.testFile];
|
97
|
+
}
|
98
|
+
return new this(Object.extend({tests: $A(tests)}, options));
|
99
|
+
},
|
100
|
+
|
101
|
+
_findTestsFromDir: function(rootDir) {
|
102
|
+
var testFiles = [];
|
103
|
+
var entries = rootDir.directoryEntries;
|
104
|
+
while (entries.hasMoreElements()) {
|
105
|
+
var entry = entries.getNext().QueryInterface(Ci.nsIFile);
|
106
|
+
if (entry.isDirectory()) {
|
107
|
+
testFiles = testFiles.concat(this._findTestsFromDir(entry));
|
108
|
+
} else if (this.testFileMatcher.test(entry.path)) {
|
109
|
+
testFiles.push(entry);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
return testFiles;
|
113
|
+
}
|
114
|
+
});
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Copyright (c) 2005-2008 Sam Stephenson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
11
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
13
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
14
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
15
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
16
|
+
SOFTWARE.
|