jasmine-headless-webkit 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +3 -1
  3. data/Guardfile +7 -2
  4. data/Rakefile +1 -0
  5. data/bin/jasmine-headless-webkit +15 -2
  6. data/ext/jasmine-webkit-specrunner/ConsoleOutput.cpp +140 -0
  7. data/ext/jasmine-webkit-specrunner/ConsoleOutput.h +38 -0
  8. data/ext/jasmine-webkit-specrunner/ConsoleOutput_test.cpp +129 -0
  9. data/ext/jasmine-webkit-specrunner/ConsoleOutput_test.h +32 -0
  10. data/ext/jasmine-webkit-specrunner/ConsoleOutput_test.pro +7 -0
  11. data/ext/jasmine-webkit-specrunner/Makefile.dummy +5 -0
  12. data/ext/jasmine-webkit-specrunner/{HeadlessSpecRunner/Page.cpp → Page.cpp} +3 -4
  13. data/ext/jasmine-webkit-specrunner/{HeadlessSpecRunner/Page.h → Page.h} +0 -2
  14. data/ext/jasmine-webkit-specrunner/Page_test.cpp +43 -0
  15. data/ext/jasmine-webkit-specrunner/Page_test.h +27 -0
  16. data/ext/jasmine-webkit-specrunner/Page_test.pro +6 -0
  17. data/ext/jasmine-webkit-specrunner/ReportFileOutput.cpp +54 -0
  18. data/ext/jasmine-webkit-specrunner/ReportFileOutput.h +37 -0
  19. data/ext/jasmine-webkit-specrunner/ReportFileOutput_test.cpp +88 -0
  20. data/ext/jasmine-webkit-specrunner/ReportFileOutput_test.h +26 -0
  21. data/ext/jasmine-webkit-specrunner/ReportFileOutput_test.pro +7 -0
  22. data/ext/jasmine-webkit-specrunner/Runner.cpp +211 -0
  23. data/ext/jasmine-webkit-specrunner/Runner.h +66 -0
  24. data/ext/jasmine-webkit-specrunner/common.pri +9 -0
  25. data/ext/jasmine-webkit-specrunner/extconf.rb +3 -1
  26. data/ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner.pro +19 -0
  27. data/ext/jasmine-webkit-specrunner/specrunner.cpp +5 -3
  28. data/ext/jasmine-webkit-specrunner/specrunner.pro +3 -7
  29. data/ext/jasmine-webkit-specrunner/test.pri +3 -0
  30. data/ext/jasmine-webkit-specrunner/test.rb +24 -0
  31. data/jasmine/jasmine.headless-reporter.coffee +47 -15
  32. data/jasmine/jasmine.headless-reporter.js +42 -17
  33. data/jasmine-headless-webkit.gemspec +2 -2
  34. data/js-lib/beautify-html.js +463 -0
  35. data/js-lib/jsDump.js +209 -0
  36. data/lib/jasmine/files_list.rb +35 -64
  37. data/lib/jasmine/headless/cacheable_action.rb +77 -0
  38. data/lib/jasmine/headless/coffee_script_cache.rb +20 -0
  39. data/lib/jasmine/headless/errors.rb +1 -0
  40. data/lib/jasmine/headless/options.rb +14 -2
  41. data/lib/jasmine/headless/railtie.rb +0 -2
  42. data/lib/jasmine/headless/report.rb +76 -0
  43. data/lib/jasmine/headless/report_message/console.rb +20 -0
  44. data/lib/jasmine/headless/report_message/error.rb +20 -0
  45. data/lib/jasmine/headless/report_message/fail.rb +5 -0
  46. data/lib/jasmine/headless/report_message/pass.rb +5 -0
  47. data/lib/jasmine/headless/report_message/spec.rb +30 -0
  48. data/lib/jasmine/headless/report_message/total.rb +31 -0
  49. data/lib/jasmine/headless/report_message.rb +11 -0
  50. data/lib/jasmine/headless/runner.rb +2 -6
  51. data/lib/jasmine/headless/spec_file_analyzer.rb +37 -0
  52. data/lib/jasmine/headless/task.rb +0 -2
  53. data/lib/jasmine/headless/version.rb +5 -0
  54. data/lib/jasmine/template_writer.rb +17 -2
  55. data/lib/jasmine-headless-webkit.rb +15 -2
  56. data/lib/qt/qmake.rb +61 -30
  57. data/script/gemfile +11 -0
  58. data/script/hooks/pre-commit +15 -0
  59. data/script/initialize-environment +35 -0
  60. data/script/install-git-hooks +6 -0
  61. data/spec/bin/jasmine-headless-webkit_spec.rb +10 -0
  62. data/spec/javascripts/jasmine.headless-reporter_spec.coffee +63 -5
  63. data/spec/lib/jasmine/files_list_spec.rb +37 -116
  64. data/spec/lib/jasmine/headless/cacheable_action_spec.rb +89 -0
  65. data/spec/lib/jasmine/headless/coffee_script_cache_spec.rb +21 -0
  66. data/spec/lib/jasmine/headless/options_spec.rb +3 -5
  67. data/spec/lib/jasmine/headless/report_message/spec_spec.rb +20 -0
  68. data/spec/lib/jasmine/headless/report_spec.rb +79 -0
  69. data/spec/lib/jasmine/headless/runner_spec.rb +5 -1
  70. data/spec/lib/jasmine/headless/spec_file_analyzer_spec.rb +57 -0
  71. data/spec/lib/qt/qmake_spec.rb +15 -2
  72. data/spec/spec_helper.rb +35 -22
  73. metadata +109 -19
  74. data/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.cpp +0 -0
  75. data/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/ConsoleOutput.h +0 -0
  76. data/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.cpp +0 -270
  77. data/ext/jasmine-webkit-specrunner/HeadlessSpecRunner/Runner.h +0 -61
  78. data/lib/jasmine-headless-webkit/version.rb +0 -7
data/js-lib/jsDump.js ADDED
@@ -0,0 +1,209 @@
1
+ /**
2
+ * jsDump
3
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
4
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
5
+ * Date: 5/15/2008
6
+ * @projectDescription Advanced and extensible data dumping for Javascript.
7
+ * @version 1.0.0
8
+ * @author Ariel Flesler
9
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
10
+ */
11
+ var jsDump;
12
+
13
+ (function(){
14
+ function quote( str ){
15
+ return '"' + str.toString().replace(/"/g, '\\"') + '"';
16
+ }
17
+ function literal( o ){
18
+ return o + '';
19
+ }
20
+ function join( pre, arr, post ){
21
+ var s = jsDump.separator(),
22
+ base = jsDump.indent(),
23
+ inner = jsDump.indent(1);
24
+ if( arr.join )
25
+ arr = arr.join( ',' + s + inner );
26
+ if( !arr )
27
+ return pre + post;
28
+ return [ pre, inner + arr, base + post ].join(s);
29
+ }
30
+ function array( arr ){
31
+ var i = arr.length, ret = Array(i);
32
+ this.up();
33
+ while( i-- )
34
+ ret[i] = this.parse( arr[i] );
35
+ this.down();
36
+ return join( '[', ret, ']' );
37
+ }
38
+
39
+ var reName = /^function (\w+)/;
40
+
41
+ jsDump = {
42
+ doParse:function(obj) {
43
+ this.alreadySeenObjects = [];
44
+ return this.parse(obj);
45
+ },
46
+ parse:function( obj, type ){//type is used mostly internally, you can fix a (custom)type in advance
47
+ var parser = this.parsers[ type || this.typeOf(obj) ];
48
+ type = typeof parser;
49
+
50
+ return type == 'function' ? parser.call( this, obj ) :
51
+ type == 'string' ? parser :
52
+ this.parsers.error;
53
+ },
54
+ typeOf:function( obj ){
55
+ var type = typeof obj,
56
+ kind;
57
+
58
+ if ( type == 'object' || type == 'function' ) {
59
+ if ( obj === null )
60
+ return 'null';
61
+
62
+ // Extract Staff from [Object Staff]
63
+ kind = Object.prototype.toString.call(obj).slice(8, -1);
64
+ switch ( kind ) {
65
+ case 'Array':
66
+ return 'array';
67
+
68
+ case 'Date':
69
+ return 'date';
70
+
71
+ case 'RegExp':
72
+ return 'regexp';
73
+
74
+ case 'Window': //Firefox, IE, Opera
75
+ case 'DOMWindow': //WebKit
76
+ case 'global':
77
+ return 'window';
78
+
79
+ case 'HTMLDocument': //WebKit, Firefox, Opera
80
+ case 'Document': // IE
81
+ return 'document';
82
+
83
+ case 'NodeList':
84
+ return 'nodelist';
85
+
86
+ default:
87
+ if ( 'callee' in obj )
88
+ // Opera: Object.prototype.toString.call(arguments) == 'Object' :(
89
+ return 'arguments';
90
+ else if ( 'ownerDocument' in obj && 'defaultView' in obj.ownerDocument && obj instanceof obj.ownerDocument.defaultView.Node )
91
+ return 'node';
92
+ }
93
+ }
94
+ return type;
95
+ },
96
+ separator:function(){
97
+ return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
98
+ },
99
+ indent:function( extra ){// extra can be a number, shortcut for increasing-calling-decreasing
100
+ if( !this.multiline )
101
+ return '';
102
+ var chr = this.indentChar;
103
+ if( this.HTML )
104
+ chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
105
+ return Array( this._depth_ + (extra||0) ).join(chr);
106
+ },
107
+ up:function( a ){
108
+ this._depth_ += a || 1;
109
+ },
110
+ down:function( a ){
111
+ this._depth_ -= a || 1;
112
+ },
113
+ setParser:function( name, parser ){
114
+ this.parsers[name] = parser;
115
+ },
116
+ // The next 3 are exposed so you can use them
117
+ quote:quote,
118
+ literal:literal,
119
+ join:join,
120
+ _depth_: 1,
121
+ // This is the list of parsers, to modify them, use jsDump.setParser
122
+ parsers:{
123
+ window: '[Window]',
124
+ document: '[Document]',
125
+ error:'[ERROR]', //when no parser is found, shouldn't happen
126
+ unknown: '[Unknown]',
127
+ 'null':'null',
128
+ undefined:'undefined',
129
+ 'function':function( fn ){
130
+ var ret = 'function',
131
+ name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
132
+ if( name )
133
+ ret += ' ' + name;
134
+ ret += '(';
135
+ ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
136
+ return join( ret, this.parse(fn,'functionCode'), '}' );
137
+ },
138
+ array: array,
139
+ nodelist: array,
140
+ arguments: array,
141
+ object:function( map ){
142
+ var ret = [ ];
143
+ this.up();
144
+ var keys = [];
145
+ for (var key in map) { keys.push(key); }
146
+ keys = keys.sort();
147
+
148
+ for( var i = 0; i < keys.length; ++i ) {
149
+ var key = keys[i];
150
+ if (!this.ignoreFunctions || this.typeOf(map[key]) != 'function') {
151
+ var value = "<< LOOP >>";
152
+
153
+ if ((typeof map[key] != 'object') || this.alreadySeenObjects.indexOf(map[key]) == -1) {
154
+ this.alreadySeenObjects.push(map[key]);
155
+ value = this.parse(map[key]);
156
+ }
157
+
158
+ ret.push( this.parse(key,'key') + ': ' + value );
159
+ }
160
+ }
161
+ this.down();
162
+ return join( '{', ret, '}' );
163
+ },
164
+ node:function( node ){
165
+ var open = this.HTML ? '&lt;' : '<',
166
+ close = this.HTML ? '&gt;' : '>';
167
+ var tag = node.nodeName.toLowerCase(),
168
+ ret = open + tag;
169
+ for( var a in this.DOMAttrs ){
170
+ var val = node[this.DOMAttrs[a]];
171
+ if( val )
172
+ ret += ' ' + a + '=' + this.parse( val, 'attribute' );
173
+ }
174
+ return ret + close + open + '/' + tag + close;
175
+ },
176
+ functionArgs:function( fn ){//function calls it internally, it's the arguments part of the function
177
+ var l = fn.length;
178
+ if( !l ) return '';
179
+ var args = Array(l);
180
+ while( l-- )
181
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
182
+ return ' ' + args.join(', ') + ' ';
183
+ },
184
+ key:quote, //object calls it internally, the key part of an item in a map
185
+ functionCode:'[code]', //function calls it internally, it's the content of the function
186
+ attribute:quote, //node calls it internally, it's an html attribute value
187
+ string:quote,
188
+ date:quote,
189
+ regexp:literal, //regex
190
+ number:literal,
191
+ 'boolean':literal
192
+ },
193
+ DOMAttrs:{//attributes to dump from nodes, name=>realName
194
+ id:'id',
195
+ name:'name',
196
+ 'class':'className'
197
+ },
198
+ alreadySeenObjects:[],
199
+ ignoreFunctions:true,
200
+ HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
201
+ indentChar:' ',//indentation unit
202
+ multiline:true //if true, items in a collection, are separated by a \n, else just a space.
203
+ };
204
+
205
+ })();
206
+
207
+ if (typeof exports !== 'undefined') {
208
+ module.exports = jsDump;
209
+ }
@@ -1,31 +1,20 @@
1
1
  require 'jasmine-core'
2
2
  require 'iconv'
3
+ require 'time'
3
4
 
4
5
  module Jasmine
5
6
  class FilesList
6
- attr_reader :files, :filtered_files, :spec_outside_scope
7
+ attr_reader :files, :spec_files, :filtered_files, :spec_outside_scope
7
8
 
8
9
  DEFAULT_FILES = [
9
10
  File.join(Jasmine::Core.path, "jasmine.js"),
10
11
  File.join(Jasmine::Core.path, "jasmine-html.js"),
11
- File.expand_path('../../../jasmine/jasmine.headless-reporter.js', __FILE__)
12
+ File.expand_path('../../../jasmine/jasmine.headless-reporter.js', __FILE__),
13
+ File.expand_path('../../../js-lib/jsDump.js', __FILE__),
14
+ File.expand_path('../../../js-lib/beautify-html.js', __FILE__)
12
15
  ]
13
16
 
14
- class << self
15
- def get_spec_line_numbers(file)
16
- line_numbers = {}
17
-
18
- ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
19
- file.lines.each_with_index.each { |line, index|
20
- line = ic.iconv(line + ' ')[0..-2]
21
- if description = line[%r{(describe|context|it)[( ]*(["'])(.*)\2}, 3]
22
- (line_numbers[description] ||= []) << (index + 1)
23
- end
24
- }
25
-
26
- line_numbers
27
- end
28
- end
17
+ PLEASE_WAIT_IM_WORKING_TIME = 2
29
18
 
30
19
  def initialize(options = {})
31
20
  @options = options
@@ -55,7 +44,7 @@ module Jasmine
55
44
  def spec_file_line_numbers
56
45
  @spec_file_line_numbers ||= Hash[@spec_files.collect { |file|
57
46
  if File.exist?(file)
58
- if !(lines = self.class.get_spec_line_numbers(File.read(file))).empty?
47
+ if !(lines = Jasmine::Headless::SpecFileAnalyzer.for(file)).empty?
59
48
  [ file, lines ]
60
49
  end
61
50
  else
@@ -66,51 +55,31 @@ module Jasmine
66
55
 
67
56
  private
68
57
  def to_html(files)
69
- coffeescript_run = []
58
+ alert_time = Time.now + PLEASE_WAIT_IM_WORKING_TIME
70
59
 
71
60
  files.collect { |file|
72
- coffeescript_run << file if (ext = File.extname(file)) == '.coffee'
73
-
74
- output = []
75
- if (files.last == file or ext != '.coffee') and !coffeescript_run.empty?
76
- output << ensure_coffeescript_run!(coffeescript_run)
61
+ if alert_time && alert_time < Time.now
62
+ puts "Rebuilding cache, please wait..."
63
+ alert_time = nil
77
64
  end
78
65
 
79
- if ext != '.coffee'
80
- output << case File.extname(file)
81
- when '.js'
82
- %{<script type="text/javascript" src="#{file}"></script>}
83
- when '.css'
84
- %{<link rel="stylesheet" href="#{file}" type="text/css" />}
66
+ case File.extname(file)
67
+ when '.coffee'
68
+ begin
69
+ %{<script type="text/javascript">#{Jasmine::Headless::CoffeeScriptCache.for(file)}</script>}
70
+ rescue CoffeeScript::CompilationError => ne
71
+ puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), ne.message.to_s.color(:white) ]
72
+ raise ne
73
+ rescue StandardError => e
74
+ puts "[%s] Error in compiling one of the followng: %s" % [ 'coffeescript'.color(:red), files.join(' ').color(:yellow) ]
75
+ raise e
85
76
  end
77
+ when '.js'
78
+ %{<script type="text/javascript" src="#{file}"></script>}
79
+ when '.css'
80
+ %{<link rel="stylesheet" href="#{file}" type="text/css" />}
86
81
  end
87
-
88
- output
89
- }.flatten.reject(&:empty?)
90
- end
91
-
92
- def ensure_coffeescript_run!(files)
93
- data = StringIO.new
94
- files.each { |file| data << File.read(file) << "\n" }
95
- data.rewind
96
-
97
- %{<script type="text/javascript">#{CoffeeScript.compile(data)}</script>}
98
- rescue CoffeeScript::CompilationError => e
99
- files.each do |file|
100
- begin
101
- CoffeeScript.compile(fh = File.open(file))
102
- rescue CoffeeScript::CompilationError => ne
103
- puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), ne.message.to_s.color(:white) ]
104
- raise ne
105
- ensure
106
- fh.close
107
- end
108
- end
109
- rescue StandardError => e
110
- puts "[%s] Error in compiling one of the followng: %s" % [ 'coffeescript'.color(:red), files.join(' ').color(:yellow) ]
111
- raise e
112
- ensure
113
- files.clear
82
+ }.flatten.compact.reject(&:empty?)
114
83
  end
115
84
 
116
85
  def spec_filter
@@ -131,15 +100,17 @@ module Jasmine
131
100
  @files += found_files
132
101
 
133
102
  if searches == 'spec_files'
134
- @spec_files = @files + spec_filter
103
+ @spec_files += spec_filter.empty? ? found_files : (found_files & spec_filter)
135
104
  end
136
105
 
137
- @filtered_files += (if searches == 'spec_files'
138
- @spec_outside_scope = ((spec_filter | found_files).sort != found_files.sort)
139
- spec_filter.empty? ? found_files : (spec_filter || found_files)
140
- else
141
- found_files
142
- end)
106
+ @filtered_files += begin
107
+ if searches == 'spec_files'
108
+ @spec_outside_scope = ((spec_filter | found_files).sort != found_files.sort)
109
+ spec_filter.empty? ? found_files : (spec_filter || found_files)
110
+ else
111
+ found_files
112
+ end
113
+ end
143
114
  end
144
115
  end
145
116
  end
@@ -0,0 +1,77 @@
1
+ module Jasmine::Headless
2
+ class CacheableAction
3
+ class << self
4
+ def enabled=(bool)
5
+ @enabled = bool
6
+ end
7
+
8
+ def enabled?
9
+ @enabled = true if @enabled == nil
10
+ @enabled
11
+ end
12
+
13
+ def cache_type
14
+ raise ArgumentError.new("No cache type defined for #{self.name}") if @cache_type == nil
15
+ @cache_type
16
+ end
17
+
18
+ def cache_type=(type)
19
+ @cache_type = type
20
+ end
21
+
22
+ def cache_dir=(dir)
23
+ @cache_dir = dir
24
+ end
25
+
26
+ def cache_dir
27
+ @cache_dir ||= '.jhw-cache'
28
+ end
29
+
30
+ def for(file)
31
+ new(file).handle
32
+ end
33
+ end
34
+
35
+ attr_reader :file
36
+
37
+ def initialize(file)
38
+ @file = file
39
+ end
40
+
41
+ def handle
42
+ if CacheableAction.enabled?
43
+ if fresh?
44
+ unserialize(File.read(cache_file))
45
+ else
46
+ result = action
47
+ FileUtils.mkdir_p File.split(cache_file).first
48
+ File.open(cache_file, 'wb') { |fh| fh.print serialize(result) }
49
+ result
50
+ end
51
+ else
52
+ action
53
+ end
54
+ end
55
+
56
+ def cache_file
57
+ @cache_file ||= File.join(self.class.cache_dir, self.class.cache_type, Digest::SHA1.hexdigest(file))
58
+ end
59
+
60
+ def fresh?
61
+ File.exist?(cache_file) && (File.mtime(file) < File.mtime(cache_file))
62
+ end
63
+
64
+ def action
65
+ raise StandardError.new("Override action")
66
+ end
67
+
68
+ def serialize(data)
69
+ data
70
+ end
71
+
72
+ def unserialize(data)
73
+ data
74
+ end
75
+ end
76
+ end
77
+
@@ -0,0 +1,20 @@
1
+ require 'coffee_script'
2
+ require 'digest/sha1'
3
+ require 'fileutils'
4
+
5
+ module Jasmine
6
+ module Headless
7
+ class CoffeeScriptCache < CacheableAction
8
+ class << self
9
+ def cache_type
10
+ "coffee_script"
11
+ end
12
+ end
13
+
14
+ def action
15
+ CoffeeScript.compile(File.read(file))
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -10,6 +10,7 @@ module Jasmine
10
10
  class ConsoleLogUsage < StandardError ; end
11
11
 
12
12
  class JasmineConfigNotFound < Errno::ENOENT ; end
13
+ class InvalidReport < StandardError ; end
13
14
  end
14
15
  end
15
16
 
@@ -13,11 +13,13 @@ module Jasmine
13
13
  :remove_html_file => true,
14
14
  :jasmine_config => 'spec/javascripts/support/jasmine.yml',
15
15
  :report => false,
16
+ :do_list => false,
16
17
  :full_run => true,
18
+ :enable_cache => true,
17
19
  :files => []
18
20
  }
19
21
 
20
- DEFAULTS_FILE = '.jasmine-headless-webkit'
22
+ DEFAULTS_FILE = File.join(Dir.pwd, '.jasmine-headless-webkit')
21
23
  GLOBAL_DEFAULTS_FILE = File.expand_path("~/#{DEFAULTS_FILE}")
22
24
 
23
25
  def self.from_command_line
@@ -29,6 +31,7 @@ module Jasmine
29
31
 
30
32
  def initialize(opts = {})
31
33
  @options = DEFAULT_OPTIONS.dup
34
+ read_defaults_files
32
35
  opts.each { |k, v| @options[k] = v if v }
33
36
  end
34
37
 
@@ -40,6 +43,10 @@ module Jasmine
40
43
  @options[:colors] = true
41
44
  when '--no-colors', '-nc'
42
45
  @options[:colors] = false
46
+ when '--cache'
47
+ @options[:enable_cache] = true
48
+ when '--no-cache'
49
+ @options[:enable_cache] = false
43
50
  when '--keep'
44
51
  @options[:remove_html_file] = false
45
52
  when '--report'
@@ -48,6 +55,8 @@ module Jasmine
48
55
  @options[:jasmine_config] = arg
49
56
  when '--no-full-run'
50
57
  @options[:full_run] = false
58
+ when '--list', '-l'
59
+ @options[:do_list] = true
51
60
  end
52
61
  end
53
62
 
@@ -63,10 +72,13 @@ module Jasmine
63
72
  command_line_args = GetoptLong.new(
64
73
  [ '--colors', '-c', GetoptLong::NO_ARGUMENT ],
65
74
  [ '--no-colors', GetoptLong::NO_ARGUMENT ],
75
+ [ '--cache', GetoptLong::NO_ARGUMENT ],
76
+ [ '--no-t stcache', GetoptLong::NO_ARGUMENT ],
66
77
  [ '--keep', GetoptLong::NO_ARGUMENT ],
67
78
  [ '--report', GetoptLong::REQUIRED_ARGUMENT ],
68
79
  [ '--jasmine-config', '-j', GetoptLong::REQUIRED_ARGUMENT ],
69
- [ '--no-full-run', GetoptLong::NO_ARGUMENT ]
80
+ [ '--no-full-run', GetoptLong::NO_ARGUMENT ],
81
+ [ '--list', '-l', GetoptLong::NO_ARGUMENT ]
70
82
  )
71
83
 
72
84
  command_line_args.each { |*args| process_option(*args) }
@@ -1,5 +1,3 @@
1
- require 'jasmine/headless/task'
2
-
3
1
  module Jasmine
4
2
  module Headless
5
3
  class Railtie < Rails::Railtie
@@ -0,0 +1,76 @@
1
+ require 'forwardable'
2
+
3
+ module Jasmine::Headless
4
+ class Report
5
+ extend Forwardable
6
+
7
+ def_delegators :report, :length, :[]
8
+ def_delegators :last_total, :total, :failed, :time
9
+
10
+ class << self
11
+ def load(file)
12
+ new(file).process
13
+ end
14
+ end
15
+
16
+ attr_reader :file, :report
17
+
18
+ def initialize(file)
19
+ @file = file
20
+ end
21
+
22
+ def process
23
+ last_message = nil
24
+ @report = File.readlines(file).collect do |line|
25
+ type, *parts = line.split('||', -1)
26
+
27
+ if !(report_klass = report_class_for(type))
28
+ if last_message.kind_of?(Jasmine::Headless::ReportMessage::Console)
29
+ last_message.message << "\n"
30
+ last_message.message << line.strip
31
+ end
32
+ else
33
+ parts.last.strip!
34
+
35
+ last_message = report_klass.new_from_parts(parts)
36
+ end
37
+ end
38
+ self
39
+ end
40
+
41
+ def has_used_console?
42
+ @report.any? { |entry| entry.kind_of?(Jasmine::Headless::ReportMessage::Console) }
43
+ end
44
+
45
+ def has_failed_on?(statement)
46
+ @report.any? { |entry|
47
+ if entry.kind_of?(Jasmine::Headless::ReportMessage::Fail)
48
+ entry.statement == statement
49
+ end
50
+ }
51
+ end
52
+
53
+ def valid?
54
+ last_total != nil
55
+ end
56
+
57
+ def failed_files
58
+ @report.find_all { |entry|
59
+ entry.kind_of?(Jasmine::Headless::ReportMessage::Fail)
60
+ }.collect(&:filename).uniq.compact
61
+ end
62
+
63
+ private
64
+
65
+ def last_total
66
+ @report.reverse.find { |entry| entry.respond_to?(:total) }
67
+ end
68
+
69
+ def report_class_for(type)
70
+ if constant = ReportMessage.constants.find { |k| k.to_s.downcase == type.downcase }
71
+ ReportMessage.const_get(constant)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,20 @@
1
+ module Jasmine::Headless::ReportMessage
2
+ class Console
3
+ class << self
4
+ def new_from_parts(parts)
5
+ new(parts.first)
6
+ end
7
+ end
8
+
9
+ attr_reader :message
10
+
11
+ def initialize(message)
12
+ @message = message
13
+ end
14
+
15
+ def ==(other)
16
+ self.message == other.message
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,20 @@
1
+ module Jasmine::Headless::ReportMessage
2
+ class Error
3
+ class << self
4
+ def new_from_parts(parts)
5
+ new(*parts)
6
+ end
7
+ end
8
+
9
+ attr_reader :message, :file_info
10
+
11
+ def initialize(message, file_info)
12
+ @message, @file_info = message, file_info
13
+ end
14
+
15
+ def ==(other)
16
+ self.message == other.message && self.file_info == other.file_info
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,5 @@
1
+ module Jasmine::Headless::ReportMessage
2
+ class Fail < Spec
3
+ end
4
+ end
5
+
@@ -0,0 +1,5 @@
1
+ module Jasmine::Headless::ReportMessage
2
+ class Pass < Spec
3
+ end
4
+ end
5
+
@@ -0,0 +1,30 @@
1
+ module Jasmine::Headless::ReportMessage
2
+ class Spec
3
+ class << self
4
+ def new_from_parts(parts)
5
+ file_info = parts.pop
6
+
7
+ new(parts.join(' '), file_info)
8
+ end
9
+ end
10
+
11
+ attr_reader :statement, :file_info
12
+
13
+ def initialize(statement, file_info)
14
+ @statement, @file_info = statement, file_info
15
+ end
16
+
17
+ def ==(other)
18
+ self.statement == other.statement && self.file_info == other.file_info
19
+ end
20
+
21
+ def filename
22
+ if name = file_info.split(":").first
23
+ name
24
+ else
25
+ nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+