ichabod 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
});
|