ichabod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.md +54 -0
- data/Rakefile +37 -0
- data/bin/ichabod +39 -0
- data/examples/index.html +25 -0
- data/examples/jasmine/index.html +25 -0
- data/examples/jasmine/klass.js +52 -0
- data/examples/jasmine/lib/MIT.LICENSE +20 -0
- data/examples/jasmine/lib/jasmine-html.js +188 -0
- data/examples/jasmine/lib/jasmine.css +166 -0
- data/examples/jasmine/lib/jasmine.js +2421 -0
- data/examples/jasmine/model.js +206 -0
- data/examples/jasmine/model.spec.js +34 -0
- data/lib/ichabod/coercion.rb +61 -0
- data/lib/ichabod/delegate/load.rb +28 -0
- data/lib/ichabod/delegate/ui.rb +18 -0
- data/lib/ichabod/repl.rb +47 -0
- data/lib/ichabod/runtime.rb +58 -0
- data/lib/ichabod/script_object/ichabod.rb +35 -0
- data/lib/ichabod/script_object/ruby.rb +43 -0
- data/lib/ichabod/tests.rb +20 -0
- data/lib/ichabod/version.rb +9 -0
- data/lib/ichabod.rb +38 -0
- data/lib/js/jasmine.js +107 -0
- data/lib/js/qunit.js +10 -0
- metadata +80 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
Math.guid = function(){
|
2
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
3
|
+
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
4
|
+
return v.toString(16);
|
5
|
+
}).toUpperCase();
|
6
|
+
};
|
7
|
+
|
8
|
+
var Model = Klass.create();
|
9
|
+
|
10
|
+
// Alias create
|
11
|
+
Model.setup = Model.create;
|
12
|
+
|
13
|
+
Model.extend({
|
14
|
+
init: function(){
|
15
|
+
this.records = {};
|
16
|
+
this.attributes = [];
|
17
|
+
},
|
18
|
+
|
19
|
+
find: function(id){
|
20
|
+
var record = this.records[id];
|
21
|
+
if ( !record ) throw("Unknown record");
|
22
|
+
return record.dup();
|
23
|
+
},
|
24
|
+
|
25
|
+
exists: function(id){
|
26
|
+
try {
|
27
|
+
return this.find(id);
|
28
|
+
} catch (e) {
|
29
|
+
return false;
|
30
|
+
}
|
31
|
+
},
|
32
|
+
|
33
|
+
populate: function(values){
|
34
|
+
// Reset model & records
|
35
|
+
this.init();
|
36
|
+
|
37
|
+
for (var i=0, il = values.length; i < il; i++) {
|
38
|
+
var record = this.inst(values[i]);
|
39
|
+
record.newRecord = false;
|
40
|
+
this.records[record.id] = record;
|
41
|
+
}
|
42
|
+
},
|
43
|
+
|
44
|
+
select: function(callback){
|
45
|
+
var result = [];
|
46
|
+
|
47
|
+
for (var key in this.records)
|
48
|
+
if (callback(this.records[key]))
|
49
|
+
result.push(this.records[key]);
|
50
|
+
|
51
|
+
return this.dupArray(result);
|
52
|
+
},
|
53
|
+
|
54
|
+
findByAttribute: function(name, value){
|
55
|
+
for (var key in this.records)
|
56
|
+
if (this.records[key][name] == value)
|
57
|
+
return this.records[key].dup();
|
58
|
+
},
|
59
|
+
|
60
|
+
findAllByAttribute: function(name, value){
|
61
|
+
return(this.select(function(item){
|
62
|
+
return(item[name] == value);
|
63
|
+
}));
|
64
|
+
},
|
65
|
+
|
66
|
+
each: function(callback){
|
67
|
+
for (var key in this.records) {
|
68
|
+
callback(this.records[key]);
|
69
|
+
}
|
70
|
+
},
|
71
|
+
|
72
|
+
all: function(){
|
73
|
+
return this.dupArray(this.recordsValues());
|
74
|
+
},
|
75
|
+
|
76
|
+
first: function(){
|
77
|
+
var record = this.recordsValues()[0];
|
78
|
+
return(record && record.dup());
|
79
|
+
},
|
80
|
+
|
81
|
+
last: function(){
|
82
|
+
var values = this.recordsValues()
|
83
|
+
var record = values[values.length - 1];
|
84
|
+
return(record && record.dup());
|
85
|
+
},
|
86
|
+
|
87
|
+
count: function(){
|
88
|
+
return this.recordsValues().length;
|
89
|
+
},
|
90
|
+
|
91
|
+
deleteAll: function(){
|
92
|
+
for (var key in this.records)
|
93
|
+
delete this.records[key];
|
94
|
+
},
|
95
|
+
|
96
|
+
destroyAll: function(){
|
97
|
+
for (var key in this.records)
|
98
|
+
this.records[key].destroy();
|
99
|
+
},
|
100
|
+
|
101
|
+
update: function(id, atts){
|
102
|
+
this.find(id).updateAttributes(atts);
|
103
|
+
},
|
104
|
+
|
105
|
+
create: function(atts){
|
106
|
+
var record = this.inst(atts);
|
107
|
+
record.save();
|
108
|
+
return record;
|
109
|
+
},
|
110
|
+
|
111
|
+
destroy: function(id){
|
112
|
+
this.find(id).destroy();
|
113
|
+
},
|
114
|
+
|
115
|
+
// Private
|
116
|
+
|
117
|
+
recordsValues: function(){
|
118
|
+
var result = []
|
119
|
+
for (var key in this.records)
|
120
|
+
result.push(this.records[key])
|
121
|
+
return result;
|
122
|
+
},
|
123
|
+
|
124
|
+
dupArray: function(array){
|
125
|
+
return array.map(function(item){
|
126
|
+
return item.dup();
|
127
|
+
});
|
128
|
+
}
|
129
|
+
});
|
130
|
+
|
131
|
+
Model.include({
|
132
|
+
newRecord: true,
|
133
|
+
|
134
|
+
init: function(atts){
|
135
|
+
if (atts) this.load(atts);
|
136
|
+
},
|
137
|
+
|
138
|
+
isNew: function(){
|
139
|
+
return this.newRecord;
|
140
|
+
},
|
141
|
+
|
142
|
+
validate: function(){ },
|
143
|
+
|
144
|
+
load: function(attributes){
|
145
|
+
for(var name in attributes)
|
146
|
+
this[name] = attributes[name];
|
147
|
+
},
|
148
|
+
|
149
|
+
attributes: function(){
|
150
|
+
var result = {};
|
151
|
+
for(var i in this.parent.attributes) {
|
152
|
+
var attr = this.parent.attributes[i];
|
153
|
+
result[attr] = this[attr];
|
154
|
+
}
|
155
|
+
result.id = this.id;
|
156
|
+
return result;
|
157
|
+
},
|
158
|
+
|
159
|
+
eql: function(rec){
|
160
|
+
return(rec && rec.id === this.id &&
|
161
|
+
rec.parent === this.parent);
|
162
|
+
},
|
163
|
+
|
164
|
+
save: function(){
|
165
|
+
if (this.validate() == false) return false;
|
166
|
+
this.newRecord ? this.create() : this.update();
|
167
|
+
},
|
168
|
+
|
169
|
+
updateAttribute: function(name, value){
|
170
|
+
this[name] = value;
|
171
|
+
return this.save();
|
172
|
+
},
|
173
|
+
|
174
|
+
updateAttributes: function(attributes){
|
175
|
+
this.load(attributes);
|
176
|
+
return this.save();
|
177
|
+
},
|
178
|
+
|
179
|
+
destroy: function(){
|
180
|
+
delete this.parent.records[this.id];
|
181
|
+
},
|
182
|
+
|
183
|
+
dup: function(){
|
184
|
+
return Object.create(this);
|
185
|
+
},
|
186
|
+
|
187
|
+
toJSON: function(){
|
188
|
+
return(JSON.stringify(this.attributes()));
|
189
|
+
},
|
190
|
+
|
191
|
+
// Private
|
192
|
+
|
193
|
+
update: function(){
|
194
|
+
this.parent.records[this.id] = this.dup();
|
195
|
+
},
|
196
|
+
|
197
|
+
generateID: function(){
|
198
|
+
return Math.guid();
|
199
|
+
},
|
200
|
+
|
201
|
+
create: function(){
|
202
|
+
if ( !this.id ) this.id = this.generateID();
|
203
|
+
this.newRecord = false;
|
204
|
+
this.parent.records[this.id] = this.dup();
|
205
|
+
}
|
206
|
+
});
|
@@ -0,0 +1,34 @@
|
|
1
|
+
describe("Model", function(){
|
2
|
+
var Asset;
|
3
|
+
|
4
|
+
beforeEach(function(){
|
5
|
+
Asset = Model.setup();
|
6
|
+
Asset.attributes = ["name"];
|
7
|
+
});
|
8
|
+
|
9
|
+
it("can create records", function(){
|
10
|
+
var asset = Asset.create({name: "test.pdf"});
|
11
|
+
expect(Asset.first()).toEqual(asset);
|
12
|
+
});
|
13
|
+
|
14
|
+
it("can update records", function(){
|
15
|
+
var asset = Asset.create({name: "test.pdf"});
|
16
|
+
|
17
|
+
expect(Asset.first().name).toEqual("test.pdf");
|
18
|
+
|
19
|
+
asset.name = "wem.pdf";
|
20
|
+
asset.save();
|
21
|
+
|
22
|
+
expect(Asset.first().name).toEqual("wem.pdf");
|
23
|
+
});
|
24
|
+
|
25
|
+
it("can destroy records", function(){
|
26
|
+
var asset = Asset.create({name: "test.pdf"});
|
27
|
+
|
28
|
+
expect(Asset.first()).toEqual(asset);
|
29
|
+
|
30
|
+
asset.destroy();
|
31
|
+
|
32
|
+
expect(Asset.first()).toBeFalsy();
|
33
|
+
});
|
34
|
+
});
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Help convert objects WebKit's JS returns into things we can use
|
2
|
+
|
3
|
+
class NSCFNumber
|
4
|
+
def inspect
|
5
|
+
if Integer(self) == Float(self)
|
6
|
+
Integer(self).to_s
|
7
|
+
else
|
8
|
+
Float(self).to_s
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class NSCFBoolean
|
14
|
+
def inspect
|
15
|
+
(self == NSNumber.numberWithBool(true)).to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class WebScriptObject
|
20
|
+
def inspect
|
21
|
+
callWebScriptMethod("toString", withArguments:nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class WebUndefined
|
26
|
+
def &(ob)
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def ^(ob)
|
31
|
+
!ob
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
'undefined'
|
36
|
+
end
|
37
|
+
|
38
|
+
def nil?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_i
|
43
|
+
0
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_f
|
47
|
+
0.0
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
[]
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
''
|
56
|
+
end
|
57
|
+
|
58
|
+
def |(ob)
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ichabod
|
2
|
+
module Delegate
|
3
|
+
class Load
|
4
|
+
attr_reader :runtime
|
5
|
+
|
6
|
+
def initialize(runtime)
|
7
|
+
@runtime = runtime
|
8
|
+
end
|
9
|
+
|
10
|
+
def webView(sender, didFinishLoadForFrame:frame)
|
11
|
+
# Page loaded...
|
12
|
+
end
|
13
|
+
|
14
|
+
def webView(webView, didClearWindowObject:windowScriptObject, forFrame:frame)
|
15
|
+
windowScriptObject.setValue(ScriptObject::Ruby.new(runtime), forKey:"Ruby")
|
16
|
+
windowScriptObject.setValue(ScriptObject::Ichabod.new(runtime), forKey:"Ichabod")
|
17
|
+
end
|
18
|
+
|
19
|
+
def webView(view, didFailLoadWithError:error, forFrame:frame)
|
20
|
+
raise "Failed: #{error.localizedDescription}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def webView(view, didFailProvisionalLoadWithError:error, forFrame:frame)
|
24
|
+
raise "Failed: #{error.localizedDescription}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ichabod
|
2
|
+
module Delegate
|
3
|
+
class UI
|
4
|
+
attr_reader :runtime
|
5
|
+
|
6
|
+
def initialize(runtime)
|
7
|
+
@runtime = runtime
|
8
|
+
end
|
9
|
+
|
10
|
+
def webView(webView, addMessageToConsole:message)
|
11
|
+
puts message[:sourceURL] + ":" +
|
12
|
+
message[:lineNumber].to_s + " " +
|
13
|
+
message[:message]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/lib/ichabod/repl.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'readline'
|
2
|
+
|
3
|
+
module Ichabod
|
4
|
+
class Repl
|
5
|
+
Prompt = 'js> '
|
6
|
+
AwaitingInput = '?> '
|
7
|
+
Result = '=> '
|
8
|
+
HistoryFile = File.join(File.expand_path('~'), '.ichabod_history')
|
9
|
+
|
10
|
+
def self.start(dom = nil)
|
11
|
+
load_history
|
12
|
+
@parser = Ichabod::Runtime.new(:dom => dom)
|
13
|
+
|
14
|
+
loop do
|
15
|
+
input = Readline.readline(Prompt)
|
16
|
+
quit if input.nil?
|
17
|
+
|
18
|
+
begin
|
19
|
+
puts Result + @parser.eval(input).inspect.to_s
|
20
|
+
rescue => e
|
21
|
+
save_history
|
22
|
+
raise e
|
23
|
+
else
|
24
|
+
Readline::HISTORY.push(input)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.load_history
|
30
|
+
return unless File.exists? HistoryFile
|
31
|
+
File.readlines(HistoryFile).each do |line|
|
32
|
+
Readline::HISTORY.push line.chomp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.save_history
|
37
|
+
File.open(HistoryFile, 'w') do |f|
|
38
|
+
f.puts Readline::HISTORY.to_a.join("\n")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.quit
|
43
|
+
save_history
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# http://developer.apple.com/documentation/Cocoa/Reference/WebKit/ObjC_classic/index.html
|
2
|
+
|
3
|
+
module Ichabod
|
4
|
+
class Runtime
|
5
|
+
attr_reader :webView
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@webView = WebView.new
|
9
|
+
@webView.setFrameLoadDelegate(Delegate::Load.new(self))
|
10
|
+
@webView.setUIDelegate(Delegate::UI.new(self))
|
11
|
+
load_dom(options[:dom]) if options[:dom]
|
12
|
+
end
|
13
|
+
|
14
|
+
def navigate(url)
|
15
|
+
# Local file check
|
16
|
+
unless url =~ /^http/
|
17
|
+
url = "file://" + File.expand_path(url)
|
18
|
+
end
|
19
|
+
|
20
|
+
url = NSURL.alloc.initWithString(url)
|
21
|
+
@webView.mainFrame.loadRequest(NSURLRequest.requestWithURL(url))
|
22
|
+
self
|
23
|
+
end
|
24
|
+
alias_method :open, :navigate
|
25
|
+
|
26
|
+
def eval(js)
|
27
|
+
@webView.windowScriptObject.evaluateWebScript(js)
|
28
|
+
end
|
29
|
+
|
30
|
+
def eval_file(file)
|
31
|
+
file = File.expand_path(file.to_s, Ichabod::JS_PATH)
|
32
|
+
if File.exists? file
|
33
|
+
eval File.read(file)
|
34
|
+
elsif File.exists? file + '.js'
|
35
|
+
eval File.read(file + '.js')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_dom(dom, base_url = nil)
|
40
|
+
@dom = File.exists?(dom) ? File.read(dom) : dom
|
41
|
+
@webView.mainFrame.loadHTMLString(@dom, baseURL:base_url)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
@dom ? html_source : super
|
46
|
+
end
|
47
|
+
|
48
|
+
def html_source
|
49
|
+
eval("document.documentElement.outerHTML")
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
unless NSApplication.sharedApplication.running?
|
54
|
+
NSApplication.sharedApplication.run
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Ichabod
|
2
|
+
module ScriptObject
|
3
|
+
class Ichabod
|
4
|
+
attr_reader :runtime
|
5
|
+
|
6
|
+
def initialize(runtime)
|
7
|
+
@runtime = runtime
|
8
|
+
end
|
9
|
+
|
10
|
+
def open(url)
|
11
|
+
runtime.open(url)
|
12
|
+
end
|
13
|
+
|
14
|
+
def eval(js)
|
15
|
+
runtime.eval(js)
|
16
|
+
end
|
17
|
+
|
18
|
+
def exit(code = 0)
|
19
|
+
Kernel.exit(code)
|
20
|
+
end
|
21
|
+
|
22
|
+
def sleep(secs = 0)
|
23
|
+
Kernel.sleep(secs)
|
24
|
+
end
|
25
|
+
|
26
|
+
def args
|
27
|
+
ARGV
|
28
|
+
end
|
29
|
+
|
30
|
+
def invokeUndefinedMethodFromWebScript(name, withArguments:args)
|
31
|
+
send(name, *args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Ichabod
|
2
|
+
module ScriptObject
|
3
|
+
class Ruby
|
4
|
+
attr_reader :runtime
|
5
|
+
|
6
|
+
def initialize(runtime)
|
7
|
+
@runtime = runtime
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Lets us call simple ruby methods
|
12
|
+
#
|
13
|
+
# Ruby.IO_read(file)
|
14
|
+
# Ruby.puts('hi')
|
15
|
+
# Ruby.require('uri')
|
16
|
+
def invokeUndefinedMethodFromWebScript(name, withArguments:args)
|
17
|
+
if respond_to? name
|
18
|
+
send(name, *args)
|
19
|
+
elsif Kernel.respond_to? name
|
20
|
+
Kernel.send(name, *args)
|
21
|
+
elsif name =~ /^([A-Z][A-Za-z]+)_(.+)/
|
22
|
+
const = Kernel.const_get($1)
|
23
|
+
method = $2
|
24
|
+
|
25
|
+
if const.respond_to? method
|
26
|
+
const.send(method, *args)
|
27
|
+
elsif const.respond_to?("#{method}?")
|
28
|
+
const.send("#{method}?", *args)
|
29
|
+
elsif const.respond_to?("#{method}!")
|
30
|
+
const.send("#{method}!", *args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Ruby('$LOAD_PATH') => array...
|
37
|
+
# Ruby('1 + 1') => 2
|
38
|
+
def invokeDefaultMethodWithArguments(args)
|
39
|
+
eval(args[0])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Ichabod
|
2
|
+
module Tests
|
3
|
+
|
4
|
+
def qunit(url)
|
5
|
+
runtime = Ichabod::Runtime.new
|
6
|
+
runtime.navigate(url)
|
7
|
+
runtime.eval_file "./qunit"
|
8
|
+
runtime.run
|
9
|
+
end
|
10
|
+
|
11
|
+
def jasmine(url)
|
12
|
+
runtime = Ichabod::Runtime.new
|
13
|
+
runtime.navigate(url)
|
14
|
+
runtime.eval_file "./jasmine"
|
15
|
+
runtime.run
|
16
|
+
end
|
17
|
+
|
18
|
+
extend self
|
19
|
+
end
|
20
|
+
end
|
data/lib/ichabod.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# It's pretty much that easy.
|
3
|
+
#
|
4
|
+
|
5
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE != 'macruby'
|
6
|
+
abort "Ichabod requires MacRuby. http://www.macruby.org/"
|
7
|
+
end
|
8
|
+
|
9
|
+
framework 'WebKit'
|
10
|
+
|
11
|
+
require 'ichabod/runtime'
|
12
|
+
require 'ichabod/coercion'
|
13
|
+
require 'ichabod/script_object/ruby'
|
14
|
+
require 'ichabod/script_object/ichabod'
|
15
|
+
require 'ichabod/delegate/ui'
|
16
|
+
require 'ichabod/delegate/load'
|
17
|
+
require 'ichabod/tests'
|
18
|
+
|
19
|
+
module Ichabod
|
20
|
+
JS_PATH = File.join(File.dirname(__FILE__), "js")
|
21
|
+
|
22
|
+
def self.eval(js)
|
23
|
+
Runtime.new.eval(js)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.eval_file(file)
|
27
|
+
contents = File.read(File.expand_path(file))
|
28
|
+
eval(contents)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(dom)
|
32
|
+
Runtime.new(:dom => dom).run
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.open(url)
|
36
|
+
Runtime.new.open(url).run
|
37
|
+
end
|
38
|
+
end
|
data/lib/js/jasmine.js
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
// Jasmine Reporter mostly inspired/stolen from the jasmine-node project
|
2
|
+
// which is under the (under the MIT license)
|
3
|
+
// https://github.com/cpojer/jasmine-node
|
4
|
+
|
5
|
+
window.addEventListener("load", function(){
|
6
|
+
jasmine.printRunnerResults = function(runner){
|
7
|
+
var results = runner.results();
|
8
|
+
var suites = runner.suites();
|
9
|
+
var msg = '';
|
10
|
+
msg += suites.length + ' test' + ((suites.length === 1) ? '' : 's') + ', ';
|
11
|
+
msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', ';
|
12
|
+
msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + '\n';
|
13
|
+
return msg;
|
14
|
+
};
|
15
|
+
|
16
|
+
var ansi = {
|
17
|
+
green: '\033[32m',
|
18
|
+
red: '\033[31m',
|
19
|
+
yellow: '\033[33m',
|
20
|
+
none: '\033[0m'
|
21
|
+
};
|
22
|
+
|
23
|
+
var noop = function(){};
|
24
|
+
|
25
|
+
var ConsoleReporter = function(){};
|
26
|
+
ConsoleReporter.fn = ConsoleReporter.prototype;
|
27
|
+
var C = ConsoleReporter;
|
28
|
+
|
29
|
+
C.fn.start = 0;
|
30
|
+
C.fn.columnCounter = 0;
|
31
|
+
C.fn.elapsed = 0;
|
32
|
+
C.fn.record = [];
|
33
|
+
|
34
|
+
C.fn.log = function(str){
|
35
|
+
Ruby.puts(str);
|
36
|
+
console.log(str)
|
37
|
+
};
|
38
|
+
|
39
|
+
C.fn.print = function(str){
|
40
|
+
Ruby.print(str);
|
41
|
+
};
|
42
|
+
|
43
|
+
C.fn.reportRunnerStarting = function(runner) {
|
44
|
+
this.log('Started');
|
45
|
+
this.start = Number(new Date);
|
46
|
+
};
|
47
|
+
|
48
|
+
C.fn.reportSuiteResults = function(suite) {
|
49
|
+
var specResults = suite.results();
|
50
|
+
var path = [];
|
51
|
+
while (suite) {
|
52
|
+
path.unshift(suite.description);
|
53
|
+
suite = suite.parentSuite;
|
54
|
+
}
|
55
|
+
var description = path.join(' ');
|
56
|
+
this.record.push('Spec ' + description);
|
57
|
+
|
58
|
+
specResults.items_.forEach(function(spec){
|
59
|
+
if (spec.failedCount > 0 && spec.description) {
|
60
|
+
this.record.push(' it ' + spec.description);
|
61
|
+
spec.items_.forEach(function(result){
|
62
|
+
this.record.push(' ' + result.trace.stack + '\n');
|
63
|
+
});
|
64
|
+
}
|
65
|
+
});
|
66
|
+
};
|
67
|
+
|
68
|
+
C.fn.reportSpecResults = function(spec) {
|
69
|
+
var result = spec.results();
|
70
|
+
var msg = '';
|
71
|
+
if (result.passed()) {
|
72
|
+
msg = ansi.green + '.' + ansi.none;
|
73
|
+
} else {
|
74
|
+
msg = ansi.red + 'F' + ansi.none;
|
75
|
+
}
|
76
|
+
this.print(msg);
|
77
|
+
if (this.columnCounter ++ < 50) return;
|
78
|
+
this.columnCounter = 0;
|
79
|
+
this.print('\n');
|
80
|
+
};
|
81
|
+
|
82
|
+
C.fn.reportRunnerResults = function(runner) {
|
83
|
+
this.elapsed = (Number(new Date) - this.start) / 1000;
|
84
|
+
this.log('\n');
|
85
|
+
|
86
|
+
var self = this;
|
87
|
+
this.record.forEach(function(log){
|
88
|
+
self.log(log);
|
89
|
+
});
|
90
|
+
|
91
|
+
this.log('Finished in ' + this.elapsed + ' seconds');
|
92
|
+
|
93
|
+
var summary = jasmine.printRunnerResults(runner);
|
94
|
+
if(runner.results().failedCount === 0 )
|
95
|
+
this.log(ansi.green + summary + ansi.none);
|
96
|
+
else
|
97
|
+
this.log(ansi.red + summary + ansi.none);
|
98
|
+
|
99
|
+
(this.done||noop)(runner, this.log);
|
100
|
+
|
101
|
+
Ichabod.exit();
|
102
|
+
};
|
103
|
+
|
104
|
+
C.fn.reportSpecStarting = function(){};
|
105
|
+
|
106
|
+
window.jasmine.getEnv().addReporter(new ConsoleReporter);
|
107
|
+
});
|