refactored unit tests quite a bit. Added JS and CSS fixtures.

This commit is contained in:
Tobie Langel 2008-04-23 09:55:23 +02:00
parent 81de0e3a77
commit cf5d18097e
19 changed files with 917 additions and 875 deletions

View File

@ -9,7 +9,7 @@ PROTOTYPE_VERSION = '1.6.0.2'
task :default => [:dist, :dist_helper, :package, :clean_package_source]
desc "Builds the distribution"
desc "Builds the distribution."
task :dist do
$:.unshift File.join(PROTOTYPE_ROOT, 'lib')
require 'protodoc'
@ -21,7 +21,7 @@ task :dist do
end
end
desc "Builds the updating helper"
desc "Builds the updating helper."
task :dist_helper do
$:.unshift File.join(PROTOTYPE_ROOT, 'lib')
require 'protodoc'
@ -45,8 +45,8 @@ Rake::PackageTask.new('prototype', PROTOTYPE_VERSION) do |package|
)
end
desc "Builds the distribution, runs the JavaScript unit tests and collects their results."
task :test => [:build_tests, :dist, :test_units]
desc "Builds the distribution and the test suite, runs the tests and collects their results."
task :test => [:dist, :test_units]
require 'test/lib/jstest'
desc "Runs all the JavaScript unit tests and collects the results"
@ -58,19 +58,16 @@ JavaScriptTestTask.new(:test_units) do |t|
t.mount("/dist")
t.mount("/test")
Dir["test/unit/tmp/*_test.html"].sort.each do |test_file|
tests = testcases ? { :url => "/#{test_file}", :testcases => testcases } : "/#{test_file}"
test_filename = test_file[/.*\/(.+?)_test\.html/, 1]
t.run(tests) unless tests_to_run && !tests_to_run.include?(test_filename)
Dir["test/unit/*_test.js"].each do |file|
TestBuilder.new(file).render
test_file = File.basename(file, ".js")
test_name = test_file.sub("_test", "")
unless tests_to_run && !tests_to_run.include?(test_name)
t.run("/test/unit/tmp/#{test_file}.html", testcases)
end
end
%w( safari firefox ie konqueror opera ).each do |browser|
t.browser(browser.to_sym) unless browsers_to_test && !browsers_to_test.include?(browser)
end
end
task :build_tests do
Dir["test/unit/*_test.js"].each do |test_file|
TestBuilder.new(test_file).render
end
end

View File

@ -278,12 +278,7 @@ class JavaScriptTestTask < ::Rake::TaskLib
@server = WEBrick::HTTPServer.new(:Port => 4711) # TODO: make port configurable
@server.mount_proc("/results") do |req, res|
@queue.push({
:tests => req.query['tests'].to_i,
:assertions => req.query['assertions'].to_i,
:failures => req.query['failures'].to_i,
:errors => req.query['errors'].to_i
})
@queue.push(req)
res.body = "OK"
end
@server.mount("/response", BasicServlet)
@ -303,43 +298,23 @@ class JavaScriptTestTask < ::Rake::TaskLib
@browsers.each do |browser|
if browser.supported?
t0 = Time.now
results = {:tests => 0, :assertions => 0, :failures => 0, :errors => 0}
errors = []
failures = []
test_suite_results = TestSuiteResults.new
browser.setup
puts "\nStarted tests in #{browser}"
puts "\nStarted tests in #{browser}."
@tests.each do |test|
params = "resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f)
if test.is_a?(Hash)
params << "&tests=#{test[:testcases]}" if test[:testcases]
test = test[:url]
end
browser.visit("http://localhost:4711#{test}?#{params}")
result = @queue.pop
result.each { |k, v| results[k] += v }
value = "."
if result[:failures] > 0
value = "F"
failures.push(test)
end
if result[:errors] > 0
value = "E"
errors.push(test)
end
print value
browser.visit(get_url(test))
results = TestResults.new(@queue.pop.query)
print results
test_suite_results.add(results, test[:url])
end
puts "\nFinished in #{(Time.now - t0).round.to_s} seconds."
puts " Failures: #{failures.join(', ')}" unless failures.empty?
puts " Errors: #{errors.join(', ')}" unless errors.empty?
puts "#{results[:tests]} tests, #{results[:assertions]} assertions, #{results[:failures]} failures, #{results[:errors]} errors"
print "\nFinished in #{Time.now - t0} seconds."
print test_suite_results
browser.teardown
else
puts "\nSkipping #{browser}, not supported on this OS"
puts "\nSkipping #{browser}, not supported on this OS."
end
end
@ -347,7 +322,13 @@ class JavaScriptTestTask < ::Rake::TaskLib
t.join
end
end
def get_url(test)
params = "resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f)
params << "&tests=#{test[:testcases]}" unless test[:testcases] == :all
"http://localhost:4711#{test[:url]}?#{params}"
end
def mount(path, dir=nil)
dir = Dir.pwd + path unless dir
@ -355,10 +336,11 @@ class JavaScriptTestTask < ::Rake::TaskLib
@server.mount(path, NonCachingFileHandler, dir)
end
# test should be specified as a url or as a hash of the form
# {:url => "url", :testcases => "testFoo,testBar"}
def run(test)
@tests<<test
# test should be specified as a hash of the form
# {:url => "url", :testcases => "testFoo,testBar"}.
# specifying :testcases is optional
def run(url, testcases = :all)
@tests << { :url => url, :testcases => testcases }
end
def browser(browser)
@ -382,37 +364,104 @@ class JavaScriptTestTask < ::Rake::TaskLib
end
end
class TestResults
attr_reader :tests, :assertions, :failures, :errors
def initialize(query)
@tests = query['tests'].to_i
@assertions = query['assertions'].to_i
@failures = query['failures'].to_i
@errors = query['errors'].to_i
end
def error?
@errors > 0
end
def failure?
@failures > 0
end
def to_s
return "E" if error?
return "F" if failure?
"."
end
end
class TestSuiteResults
def initialize
@tests = 0
@assertions = 0
@failures = 0
@errors = 0
@error_files = []
@failure_files = []
end
def add(result, file)
@tests += result.tests
@assertions += result.assertions
@failures += result.failures
@errors += result.errors
@error_files.push(file) if result.error?
@failure_files.push(file) if result.failure?
end
def error?
@errors > 0
end
def failure?
@failures > 0
end
def to_s
str = ""
str << "\n Failures: #{@failure_files.join(', ')}" if failure?
str << "\n Errors: #{@error_files.join(', ')}" if error?
"#{str}\n#{summary}\n\n"
end
def summary
"#{@tests} tests, #{@assertions} assertions, #{@failures} failures, #{@errors} errors."
end
end
class TestBuilder
UNITTEST_DIR = File.expand_path('test')
TEMPLATE = File.join(UNITTEST_DIR, 'lib', 'template.erb')
FIXTURES_EXTENSION = "html"
FIXTURES_DIR = File.join(UNITTEST_DIR, 'unit', 'fixtures')
def initialize(filename)
@filename = filename
@js_filename = File.basename(@filename)
@basename = @js_filename.sub("_test.js", "")
@fixtures_filename = "#{@basename}.#{FIXTURES_EXTENSION}"
@title = @basename.gsub("_", " ").strip.capitalize
@html_fixtures = html_fixtures
@js_fixtures_filename = external_fixtures("js")
@css_fixtures_filename = external_fixtures("css")
end
def find_fixtures
@fixtures = ""
file = File.join(FIXTURES_DIR, @fixtures_filename)
if File.exists?(file)
File.open(file).each { |line| @fixtures << line }
end
def html_fixtures
content = ""
file = File.join(FIXTURES_DIR, "#{@basename}.html")
File.open(file).each { |l| content << l } if File.exists?(file)
content
end
def external_fixtures(type)
filename = "#{@basename}.#{type}"
File.exists?(File.join(FIXTURES_DIR, filename)) ? filename : nil
end
def render
find_fixtures
File.open(destination, "w+") do |file|
file << ERB.new(IO.read(TEMPLATE), nil, "%").result(binding)
end
end
def destination
basename = File.basename(@filename, ".js")
File.join(UNITTEST_DIR, 'unit', 'tmp', "#{basename}.html")
filename = File.basename(@filename, ".js")
File.join(UNITTEST_DIR, 'unit', 'tmp', "#{filename}.html")
end
end

View File

@ -6,13 +6,21 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script type="text/javascript" charset="utf-8">
var eventResults = {};
var originalElement = Element;
var originalElement = window.Element;
</script>
<script src="../../../dist/prototype.js" type="text/javascript"></script>
<script src="../../lib/unittest.js" type="text/javascript"></script>
<script src="../<%= @js_filename %>" type="text/javascript"></script>
<link rel="stylesheet" href="../../test.css" type="text/css" />
<% if @css_fixtures_filename %>
<link rel="stylesheet" href="../fixtures/<%= @css_fixtures_filename %>" type="text/css" charset="utf-8" />
<% end %>
<% if @js_fixtures_filename %>
<script src="../fixtures/<%= @js_fixtures_filename %>" type="text/javascript" charset="utf-8"></script>
<% end %>
<script src="../<%= @js_filename %>" type="text/javascript"></script>
</head>
<body>
<h1>Prototype Unit test file</h1>
@ -20,12 +28,13 @@
<!-- This file is programmatically generated. Do not attempt to modify it. Instead, modify <%= @fixtures_filename %> -->
<!-- Log output -->
<!-- Log output start -->
<div id="testlog"></div>
<!-- Log output end -->
<!-- Fixtures start -->
<%= @fixtures %>
<!-- Fixtures end -->
<!-- HTML Fixtures start -->
<%= @html_fixtures %>
<!-- HTML Fixtures end -->
</body>
</html>
<script type="text/javascript" charset="utf-8">

View File

@ -1,38 +1,3 @@
var Fixtures = {
js: {
responseBody: '$("content").update("<H2>Hello world!</H2>");',
'Content-Type': ' text/javascript '
},
html: {
responseBody: "Pack my box with <em>five dozen</em> liquor jugs! " +
"Oh, how <strong>quickly</strong> daft jumping zebras vex..."
},
xml: {
responseBody: '<?xml version="1.0" encoding="UTF-8" ?><name attr="foo">bar</name>',
'Content-Type': 'application/xml'
},
json: {
responseBody: '{\n\r"test": 123}',
'Content-Type': 'application/json'
},
jsonWithoutContentType: {
responseBody: '{"test": 123}'
},
invalidJson: {
responseBody: '{});window.attacked = true;({}',
'Content-Type': 'application/json'
},
headerJson: {
'X-JSON': '{"test": "hello #éà"}'
}
};
var extendDefault = function(options) {
return Object.extend({
asynchronous: false,
@ -41,14 +6,6 @@ var extendDefault = function(options) {
}, options);
};
var responderCounter = 0;
// lowercase comparison because of MSIE which presents HTML tags in uppercase
var sentence = ("Pack my box with <em>five dozen</em> liquor jugs! " +
"Oh, how <strong>quickly</strong> daft jumping zebras vex...").toLowerCase();
var message = 'You must be running your tests from rake to test this feature.';
new Test.Unit.Runner({
setup: function() {
$('content').update('');

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
var testVar = 'to be updated', testVar2 = '';
var getInnerHTML = function(id) {
return $(id).innerHTML.toString().toLowerCase().gsub(/[\r\n\t]/, '');
};
@ -7,23 +6,6 @@ var createParagraph = function(text) {
p.appendChild(document.createTextNode(text));
return p;
}
Element.addMethods({
hashBrowns: function(element) { return 'hash browns'; }
});
Element.addMethods("LI", {
pancakes: function(element) { return "pancakes"; }
});
Element.addMethods("DIV", {
waffles: function(element) { return "waffles"; }
});
Element.addMethods($w("li div"), {
orangeJuice: function(element) { return "orange juice"; }
});
var documentViewportProperties;
new Test.Unit.Runner({
setup: function() {

View File

@ -1,6 +1,3 @@
Form.Element.Methods.coffee = Prototype.K;
Element.addMethods();
new Test.Unit.Runner({
testInput: function() {
this.assert($("input").present != null);

View File

@ -1,27 +1,3 @@
var Fixtures = {
People: [
{name: 'Sam Stephenson', nickname: 'sam-'},
{name: 'Marcel Molina Jr.', nickname: 'noradio'},
{name: 'Scott Barron', nickname: 'htonl'},
{name: 'Nicholas Seckar', nickname: 'Ulysses'}
],
Nicknames: $w('sam- noradio htonl Ulysses'),
Basic: [1, 2, 3],
Primes: [
1, 2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
71, 73, 79, 83, 89, 97
],
Z: []
};
for (var i = 1; i <= 100; i++)
Fixtures.Z.push(i);
function prime(value) {
for (var i = 2; i < value; i++)
if (value % i == 0) return false;

View File

@ -0,0 +1,42 @@
var Fixtures = {
js: {
responseBody: '$("content").update("<H2>Hello world!</H2>");',
'Content-Type': ' text/javascript '
},
html: {
responseBody: "Pack my box with <em>five dozen</em> liquor jugs! " +
"Oh, how <strong>quickly</strong> daft jumping zebras vex..."
},
xml: {
responseBody: '<?xml version="1.0" encoding="UTF-8" ?><name attr="foo">bar</name>',
'Content-Type': 'application/xml'
},
json: {
responseBody: '{\n\r"test": 123}',
'Content-Type': 'application/json'
},
jsonWithoutContentType: {
responseBody: '{"test": 123}'
},
invalidJson: {
responseBody: '{});window.attacked = true;({}',
'Content-Type': 'application/json'
},
headerJson: {
'X-JSON': '{"test": "hello #éà"}'
}
};
var responderCounter = 0;
// lowercase comparison because of MSIE which presents HTML tags in uppercase
var sentence = ("Pack my box with <em>five dozen</em> liquor jugs! " +
"Oh, how <strong>quickly</strong> daft jumping zebras vex...").toLowerCase();
var message = 'You must be running your tests from rake to test this feature.';

106
test/unit/fixtures/base.js Normal file
View File

@ -0,0 +1,106 @@
var Person = function(name){
this.name = name;
};
Person.prototype.toJSON = function() {
return '-' + this.name;
};
var arg1 = 1;
var arg2 = 2;
var arg3 = 3;
function TestObj() { };
TestObj.prototype.assertingEventHandler =
function(event, assertEvent, assert1, assert2, assert3, a1, a2, a3) {
assertEvent(event);
assert1(a1);
assert2(a2);
assert3(a3);
};
var globalBindTest = null;
// base class
var Animal = Class.create({
initialize: function(name) {
this.name = name;
},
name: "",
eat: function() {
return this.say("Yum!");
},
say: function(message) {
return this.name + ": " + message;
}
});
// subclass that augments a method
var Cat = Class.create(Animal, {
eat: function($super, food) {
if (food instanceof Mouse) return $super();
else return this.say("Yuk! I only eat mice.");
}
});
// empty subclass
var Mouse = Class.create(Animal, {});
//mixins
var Sellable = {
getValue: function(pricePerKilo) {
return this.weight * pricePerKilo;
},
inspect: function() {
return '#<Sellable: #{weight}kg>'.interpolate(this);
}
};
var Reproduceable = {
reproduce: function(partner) {
if (partner.constructor != this.constructor || partner.sex == this.sex)
return null;
var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
return new this.constructor('baby', weight, sex);
}
};
// base class with mixin
var Plant = Class.create(Sellable, {
initialize: function(name, weight) {
this.name = name;
this.weight = weight;
},
inspect: function() {
return '#<Plant: #{name}>'.interpolate(this);
}
});
// subclass with mixin
var Dog = Class.create(Animal, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
}
});
// subclass with mixins
var Ox = Class.create(Animal, Sellable, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
},
eat: function(food) {
if (food instanceof Plant)
this.weight += food.weight;
},
inspect: function() {
return '#<Ox: #{name}>'.interpolate(this);
}
});

View File

@ -0,0 +1,84 @@
#style_test_1 { cursor: pointer; font-size:12px;}
div.style-test { margin-left: 1px }
#style_test_dimensions_container {
position: absolute;
top: 0;
left: 500px;
width: 20px;
height: 30px;
margin: 10px;
padding: 10px;
border: 3px solid red;
}
#not_floating_style { float: none }
#floating_style { float: left }
#op2 { opacity:0.5;filter:alpha(opacity=50)progid:DXImageTransform.Microsoft.Blur(strength=10);}
#scroll_test_1 {
margin: 10px;
padding: 10px;
position: relative;
}
#scroll_test_2 {
position: absolute;
left: 10px;
top: 10px;
}
#dimensions-visible,
#dimensions-display-none,
#dimensions-visible-pos-rel,
#dimensions-display-none-pos-rel,
#dimensions-visible-pos-abs,
#dimensions-display-none-pos-abs {
font-size: 10px;
height: 10em;
width: 20em;
}
#dimensions-visible-pos-abs,
#dimensions-display-none-pos-abs {
position: absolute;
top: 15px;
left: 15px;
}
#dimensions-visible-pos-rel,
#dimensions-display-none-pos-rel {
position: relative;
top: 15px;
left: 15px;
}
#dimensions-display-none, #imensions-display-none-pos-rel, #dimensions-display-none-pos-abs {
display: none;
}
#dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td {
font-size: 10px;
margin: 0;
padding: 0;
border: 0;
border-spacing: 0;
height: 10em;
width: 20em;
}
#notInlineAbsoluted { position: absolute; }
#elementToViewportDimensions {
position: absolute;
top: 0;
left: 0;
height: 10px;
width: 10px;
background: #000;
}
/* for scroll test on really big screens */
body {
height: 40000px;
}

View File

@ -1,92 +1,3 @@
<style type="text/css" media="screen">
/* <![CDATA[ */
#style_test_1 { cursor: pointer; font-size:12px;}
div.style-test { margin-left: 1px }
#style_test_dimensions_container {
position: absolute;
top: 0;
left: 500px;
width: 20px;
height: 30px;
margin: 10px;
padding: 10px;
border: 3px solid red;
}
#not_floating_style { float: none }
#floating_style { float: left }
#op2 { opacity:0.5;filter:alpha(opacity=50)progid:DXImageTransform.Microsoft.Blur(strength=10);}
#scroll_test_1 {
margin: 10px;
padding: 10px;
position: relative;
}
#scroll_test_2 {
position: absolute;
left: 10px;
top: 10px;
}
#dimensions-visible,
#dimensions-display-none,
#dimensions-visible-pos-rel,
#dimensions-display-none-pos-rel,
#dimensions-visible-pos-abs,
#dimensions-display-none-pos-abs {
font-size: 10px;
height: 10em;
width: 20em;
}
#dimensions-visible-pos-abs,
#dimensions-display-none-pos-abs {
position: absolute;
top: 15px;
left: 15px;
}
#dimensions-visible-pos-rel,
#dimensions-display-none-pos-rel {
position: relative;
top: 15px;
left: 15px;
}
#dimensions-display-none, #imensions-display-none-pos-rel, #dimensions-display-none-pos-abs {
display: none;
}
#dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td {
font-size: 10px;
margin: 0;
padding: 0;
border: 0;
border-spacing: 0;
height: 10em;
width: 20em;
}
#notInlineAbsoluted { position: absolute; }
#elementToViewportDimensions {
position: absolute;
top: 0;
left: 0;
height: 10px;
width: 10px;
background: #000;
}
/* for scroll test on really big screens */
body {
height: 40000px;
}
/* ]]> */
</style>
<div id="scroll_test_1">
<p id="scroll_test_2">Scroll test</p>
</div>

17
test/unit/fixtures/dom.js Normal file
View File

@ -0,0 +1,17 @@
var testVar = 'to be updated', testVar2 = '', documentViewportProperties;
Element.addMethods({
hashBrowns: function(element) { return 'hash browns'; }
});
Element.addMethods("LI", {
pancakes: function(element) { return "pancakes"; }
});
Element.addMethods("DIV", {
waffles: function(element) { return "waffles"; }
});
Element.addMethods($w("li div"), {
orangeJuice: function(element) { return "orange juice"; }
});

View File

@ -0,0 +1,2 @@
Form.Element.Methods.coffee = Prototype.K;
Element.addMethods();

View File

@ -0,0 +1,23 @@
var Fixtures = {
People: [
{name: 'Sam Stephenson', nickname: 'sam-'},
{name: 'Marcel Molina Jr.', nickname: 'noradio'},
{name: 'Scott Barron', nickname: 'htonl'},
{name: 'Nicholas Seckar', nickname: 'Ulysses'}
],
Nicknames: $w('sam- noradio htonl Ulysses'),
Basic: [1, 2, 3],
Primes: [
1, 2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
71, 73, 79, 83, 89, 97
],
Z: []
};
for (var i = 1; i <= 100; i++)
Fixtures.Z.push(i);

View File

@ -0,0 +1,25 @@
var Fixtures = {
one: { a: 'A#' },
many: {
a: 'A',
b: 'B',
c: 'C',
d: 'D#'
},
functions: {
quad: function(n) { return n*n },
plus: function(n) { return n+n }
},
multiple: { color: $w('r g b') },
multiple_nil: { color: ['r', null, 'g', undefined, 0] },
multiple_all_nil: { color: [null, undefined] },
multiple_empty: { color: [] },
multiple_special: { 'stuff[]': $w('$ a ;') },
value_undefined: { a:"b", c:undefined },
value_null: { a:"b", c:null },
value_zero: { a:"b", c:0 }
};

View File

@ -0,0 +1,8 @@
var attackTarget;
var evalScriptsCounter = 0,
largeTextEscaped = '&lt;span&gt;test&lt;/span&gt;',
largeTextUnescaped = '<span>test</span>';
(2048).times(function(){
largeTextEscaped += ' ABC';
largeTextUnescaped += ' ABC';
});

View File

@ -1,29 +1,3 @@
var Fixtures = {
one: { a: 'A#' },
many: {
a: 'A',
b: 'B',
c: 'C',
d: 'D#'
},
functions: {
quad: function(n) { return n*n },
plus: function(n) { return n+n }
},
multiple: { color: $w('r g b') },
multiple_nil: { color: ['r', null, 'g', undefined, 0] },
multiple_all_nil: { color: [null, undefined] },
multiple_empty: { color: [] },
multiple_special: { 'stuff[]': $w('$ a ;') },
value_undefined: { a:"b", c:undefined },
value_null: { a:"b", c:null },
value_zero: { a:"b", c:0 }
};
new Test.Unit.Runner({
testSet: function() {
var h = $H({a: 'A'})

View File

@ -1,12 +1,3 @@
var attackTarget;
var evalScriptsCounter = 0,
largeTextEscaped = '&lt;span&gt;test&lt;/span&gt;',
largeTextUnescaped = '<span>test</span>';
(2048).times(function(){
largeTextEscaped += ' ABC';
largeTextUnescaped += ' ABC';
});
new Test.Unit.Runner({
testInterpret: function(){
this.assertIdentical('true', String.interpret(true));