shenandoah 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/.gitignore +1 -0
  2. data/ChangeLog.markdown +9 -0
  3. data/Rakefile +21 -2
  4. data/VERSION.yml +3 -2
  5. data/features/buildr.feature +29 -0
  6. data/features/example_projects/base/Rakefile +7 -0
  7. data/features/example_projects/base/lib/cells.js +105 -0
  8. data/features/example_projects/base/lib/life.js +30 -0
  9. data/features/example_projects/base/spec/cells.html +13 -0
  10. data/features/example_projects/base/spec/cells_spec.js +234 -0
  11. data/features/example_projects/base/spec/life.html +13 -0
  12. data/features/example_projects/base/spec/life_spec.js +28 -0
  13. data/features/plain-rake.feature +30 -0
  14. data/features/rails.feature +39 -0
  15. data/features/step_definitions/buildr_steps.rb +26 -0
  16. data/features/step_definitions/rails_steps.rb +38 -0
  17. data/features/step_definitions/rake_steps.rb +45 -0
  18. data/features/support/env.rb +48 -0
  19. data/lib/shenandoah/css/shenandoah.sass +138 -0
  20. data/lib/shenandoah/javascript/browser/index.js +18 -0
  21. data/lib/shenandoah/javascript/browser/multirunner-single.js +32 -0
  22. data/lib/shenandoah/javascript/browser/multirunner.js +87 -0
  23. data/lib/shenandoah/javascript/common/jquery.parsequery.js +19 -0
  24. data/lib/shenandoah/server.rb +81 -16
  25. data/lib/shenandoah/server/views/index.haml +18 -8
  26. data/lib/shenandoah/server/views/multirunner.haml +6 -0
  27. data/rails_generators/shen_spec/templates/fixture.html.erb +1 -1
  28. data/rails_generators/shenandoah/templates/application.html +1 -1
  29. data/spec/rails_generators/shen_spec_generator_spec.rb +2 -2
  30. data/spec/rails_generators/shenandoah_generator_spec.rb +3 -3
  31. data/spec/shenandoah/server_spec.rb +156 -15
  32. metadata +32 -3
  33. data/lib/shenandoah/css/screw.css +0 -90
@@ -0,0 +1,87 @@
1
+ if (!window.shenandoah) { var shenandoah = {}; }
2
+
3
+ (function ($) {
4
+ shenandoah.Multirunner = function (container_id, spec_urls) {
5
+ ////// PRIVATE
6
+
7
+ var runner = this;
8
+
9
+ function container(subselector) {
10
+ var sel = "#" + runner.container_id;
11
+ if (subselector) sel += ' ' + subselector;
12
+ return jQuery(sel);
13
+ }
14
+
15
+ ////// PUBLIC
16
+
17
+ this.container_id = container_id;
18
+ this.spec_urls = spec_urls;
19
+
20
+ this.run = function () {
21
+ this.frames = container("#frames iframe").get();
22
+ this.next();
23
+ }
24
+
25
+ this.next = function () {
26
+ if (this.frames && this.frames.length > 0) {
27
+ setTimeout(function () { jQuery(runner.frames.shift()).trigger('begin') }, 0);
28
+ } else {
29
+ var show = container('#specs li.failed').get(0) || container('#specs li').get(0);
30
+ jQuery(show).trigger('activate');
31
+ }
32
+ }
33
+
34
+ this.reset = function () {
35
+ container().empty();
36
+ this.frames = null;
37
+ }
38
+
39
+ ////// INITIALIZATION
40
+
41
+ if (!this.spec_urls || this.spec_urls.length === 0) {
42
+ container().append("<p>No specs selected to run</p>")
43
+ } else {
44
+ container().append("<ul id='specs'></ul>").append("<div id='frames'></div>");
45
+ jQuery(this.spec_urls).each(function (idx) {
46
+ container("#specs").append('<li class="spec inactive"><span class="name">' + this.replace(/\/spec\//, "") + '</span>&nbsp;<span class="count"></span></li>');
47
+ container("#frames").append('<iframe class="inactive" spec-url="' + this + '" src="about:blank"></iframe>');
48
+ var newLi = container("#specs li:last-child");
49
+ var newFrame = container("#frames iframe:last-child");
50
+ var both = jQuery(newLi.selector + ", " + newFrame.selector);
51
+
52
+ both.bind('activate', function () {
53
+ both.removeClass('inactive').addClass('active');
54
+ }).bind('deactivate', function () {
55
+ both.addClass('inactive').removeClass('active');
56
+ });
57
+
58
+ newFrame.bind("begin", null, function (evt, data) {
59
+ newLi.find('.count').empty().end().trigger('activate');
60
+ newFrame.attr('src', newFrame.attr('spec-url'));
61
+ });
62
+
63
+ newFrame.bind("complete", null, function (evt, data) {
64
+ newLi.find('.count').text(data.passed_count + " of " + data.total_count).end().
65
+ removeClass('passed').removeClass('failed').addClass(data.passed_count === data.total_count ? 'passed' : 'failed').
66
+ trigger('deactivate');
67
+ runner.next();
68
+ });
69
+
70
+ newLi.bind('click', function () {
71
+ container('#frames iframe').trigger('deactivate');
72
+ newFrame.trigger('activate');
73
+ return false;
74
+ });
75
+ });
76
+ }
77
+ };
78
+
79
+ $(document).ready(function () {
80
+ var specs = jQuery.parseQuery().spec;
81
+ if (specs && specs.constructor !== Array) {
82
+ specs = [specs];
83
+ }
84
+ new shenandoah.Multirunner('runner', specs).run();
85
+ });
86
+
87
+ }(jQuery));
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A simple querystring parser.
3
+ * Example usage: var q = $.parseQuery(); q.fooreturns "bar" if query contains "?foo=bar"; multiple values are added to an array.
4
+ * Values are unescaped by default and plus signs replaced with spaces, or an alternate processing function can be passed in the params object .
5
+ * http://actingthemaggot.com/jquery
6
+ *
7
+ * Copyright (c) 2008 Michael Manning (http://actingthemaggot.com)
8
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
9
+ * and GPL (GPL-LICENSE.txt) licenses.
10
+ **/
11
+ jQuery.parseQuery = function(qs,options) {
12
+ var q = (typeof qs === 'string'?qs:window.location.search), o = {'f':function(v){return unescape(v).replace(/\+/g,' ');}}, options = (typeof qs === 'object' && typeof options === 'undefined')?qs:options, o = jQuery.extend({}, o, options), params = {};
13
+ jQuery.each(q.match(/^\??(.*)$/)[1].split('&'),function(i,p){
14
+ p = p.split('=');
15
+ p[1] = o.f(p[1]);
16
+ params[p[0]] = params[p[0]]?((params[p[0]] instanceof Array)?(params[p[0]].push(p[1]),params[p[0]]):[params[p[0]],p[1]]):p[1];
17
+ });
18
+ return params;
19
+ }
@@ -1,5 +1,7 @@
1
1
  require 'sinatra'
2
2
  require 'haml'
3
+ require 'sass'
4
+ require 'compass'
3
5
 
4
6
  module Shenandoah
5
7
  # The server which enables in-browser execution of Screw.Unit specs in
@@ -12,18 +14,23 @@ module Shenandoah
12
14
  class Server < Sinatra::Base
13
15
  set :root, File.dirname(__FILE__) + "/server"
14
16
  set :port, 4410
17
+ set :sass, Compass.sass_engine_options
15
18
  enable :logging
16
19
 
17
20
  get '/' do
18
21
  section_map = options.locator.spec_files.
19
22
  collect { |t| t.sub(%r{^#{options.locator.spec_path}/?}, '') }.
20
23
  collect { |t| [File.dirname(t), File.basename(t).sub(/_spec.js$/, '.html')] }.
21
- inject({}) { |h, (dir, file)| h[dir] ||= []; h[dir] << file; h }
24
+ inject({}) { |h, (dir, file)| h[dir] ||= []; h[dir] << IndexEntry.new(dir, file); h }
22
25
  @sections = section_map.collect { |dir, files| [dir, files.sort] }.sort
23
26
 
24
27
  haml :index
25
28
  end
26
29
 
30
+ get '/multirunner' do
31
+ haml :multirunner
32
+ end
33
+
27
34
  get '/main/*' do
28
35
  map_file(options.locator.main_path, params[:splat].first, "Main")
29
36
  end
@@ -32,27 +39,47 @@ module Shenandoah
32
39
  map_file(options.locator.spec_path, params[:splat].first, "Spec")
33
40
  end
34
41
 
35
- get '/screw.css' do
36
- screw_css = File.join(options.locator.spec_path, 'screw.css')
37
- unless File.exist?(screw_css)
38
- screw_css = File.join(File.dirname(__FILE__), 'css/screw.css')
42
+ get '/shenandoah.css' do
43
+ shenandoah_css = File.join(options.locator.spec_path, 'shenandoah.css')
44
+ if File.exist?(shenandoah_css)
45
+ send_file shenandoah_css
46
+ else
47
+ shenandoah_sass = File.join(options.locator.spec_path, 'shenandoah.sass')
48
+ unless File.exist?(shenandoah_sass)
49
+ shenandoah_sass = File.join(File.dirname(__FILE__), "css/shenandoah.sass")
50
+ end
51
+ content_type 'text/css'
52
+ last_modified File.stat(shenandoah_sass).mtime
53
+ sass File.read(shenandoah_sass)
39
54
  end
40
- send_file screw_css
55
+ end
56
+
57
+ get '/screw.css' do
58
+ uri = '/shenandoah.css'
59
+ $stderr.puts "DEPRECATION NOTICE: Use #{uri} instead of /screw.css."
60
+ status 301
61
+ response['Location'] = uri
62
+ "This URI is deprecated. Use <a href='#{uri}'>#{uri}</a>."
41
63
  end
42
64
 
43
65
  get '/shenandoah/browser-runner.js' do
66
+ maxtime, content = concatenate_files(runner_files)
67
+
44
68
  content_type 'text/javascript'
69
+ last_modified maxtime
70
+ content
71
+ end
45
72
 
46
- last_modified runner_files.collect { |filename|
47
- File.stat("#{File.dirname(__FILE__)}/#{filename}").mtime
48
- }.max
73
+ get '/shenandoah/multirunner.js' do
74
+ maxtime, content = concatenate_files(multirunner_files)
49
75
 
50
- runner_files.collect { |filename|
51
- [
52
- "\n//////\n////// #{filename}\n//////\n",
53
- File.read("#{File.dirname(__FILE__)}/#{filename}")
54
- ]
55
- }.flatten
76
+ content_type 'text/javascript'
77
+ last_modified maxtime
78
+ content
79
+ end
80
+
81
+ get '/js/*' do
82
+ send_file File.join(File.dirname(__FILE__), "javascript/#{params[:splat].first}")
56
83
  end
57
84
 
58
85
  protected
@@ -67,6 +94,21 @@ module Shenandoah
67
94
  end
68
95
  end
69
96
 
97
+ def concatenate_files(files)
98
+ maxtime = files.collect { |filename|
99
+ File.stat("#{File.dirname(__FILE__)}/#{filename}").mtime
100
+ }.max
101
+
102
+ content = files.collect { |filename|
103
+ [
104
+ "\n//////\n////// #{filename}\n//////\n",
105
+ File.read("#{File.dirname(__FILE__)}/#{filename}")
106
+ ]
107
+ }.flatten
108
+
109
+ [maxtime, content]
110
+ end
111
+
70
112
  def runner_files # :nodoc:
71
113
  # Can't just use Dir[] because order is important
72
114
  [
@@ -81,8 +123,31 @@ module Shenandoah
81
123
  "javascript/common/smoke.mock.js",
82
124
  "javascript/common/smoke.stub.js",
83
125
  "javascript/common/screw.mocking.js",
84
- "javascript/browser/runner.js"
126
+ "javascript/browser/runner.js",
127
+ "javascript/browser/multirunner-single.js"
85
128
  ]
86
129
  end
130
+
131
+ def multirunner_files # :nodoc
132
+ [
133
+ "javascript/common/jquery-1.3.2.js",
134
+ "javascript/common/jquery.parsequery.js",
135
+ "javascript/browser/multirunner.js"
136
+ ]
137
+ end
138
+
139
+ class IndexEntry < Struct.new(:section, :test_html)
140
+ def href
141
+ '/' + ["spec", section, test_html].reject { |s| s == '.' }.join('/')
142
+ end
143
+
144
+ def name
145
+ test_html.sub(/.html$/, '')
146
+ end
147
+
148
+ def <=>(other)
149
+ self.test_html <=> other.test_html
150
+ end
151
+ end
87
152
  end
88
153
  end
@@ -1,12 +1,22 @@
1
1
  %html
2
2
  %head
3
- %link{ :rel => "stylesheet", :href => "/screw.css", :type => "text/css", :charset => "utf-8" }
4
- %body
3
+ %script{ :src => '/js/common/jquery-1.3.2.js' }
4
+ %script{ :src => '/js/browser/index.js' }
5
+ %link{ :rel => "stylesheet", :href => "/shenandoah.css", :type => "text/css", :charset => "utf-8" }
6
+ %body#index
5
7
  - if options.respond_to?(:project_name) && options.project_name
6
8
  %h1= "Specs for #{options.project_name}"
7
- - @sections.each do |section, tests|
8
- %h2= section == '.' ? '[root]' : section
9
- %ul
10
- - tests.each do |test|
11
- %li
12
- %a{ :href => ["spec", section, test].reject { |s| s == '.' }.join('/') }= test.sub(/.html$/, '')
9
+ %label#select-all
10
+ %input{ :type => 'checkbox', :name => 'select-all' }
11
+ %a select all
12
+ %form{ :method => 'GET', :action => '/multirunner' }
13
+ - @sections.each do |section, tests|
14
+ %h2= section == '.' ? '[root]' : section
15
+ %ul
16
+ - tests.each do |test|
17
+ %li
18
+ %label
19
+ %input{ :type => 'checkbox', :name => 'spec', :value => test.href }
20
+ %a{ :href => test.href }= test.name
21
+ #controls
22
+ %input{ :type => 'submit', :value => 'Run selected' }
@@ -0,0 +1,6 @@
1
+ %html
2
+ %head
3
+ %script{ :src => '/shenandoah/multirunner.js', :type => 'text/javascript' }
4
+ %link{ :rel => 'stylesheet', :href => '/shenandoah.css' }
5
+ %body
6
+ #runner
@@ -3,7 +3,7 @@
3
3
 
4
4
  <head>
5
5
  <title><%= file_name %>.js | JavaScript Testing Results</title>
6
- <link rel="stylesheet" href="/screw.css" type="text/css" charset="utf-8" />
6
+ <link rel="stylesheet" href="/shenandoah.css" type="text/css" charset="utf-8" />
7
7
  <script type="text/javascript" src="/shenandoah/browser-runner.js"></script>
8
8
  </head>
9
9
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  <head>
5
5
  <title>Application | JavaScript Testing Results</title>
6
- <link rel="stylesheet" href="/screw.css" type="text/css" charset="utf-8" />
6
+ <link rel="stylesheet" href="/shenandoah.css" type="text/css" charset="utf-8" />
7
7
  <script type="text/javascript" src="/shenandoah/browser-runner.js"></script>
8
8
  </head>
9
9
 
@@ -50,8 +50,8 @@ describe "shen_spec generator" do
50
50
  @html.should =~ %r{src="/shenandoah/browser-runner.js"}
51
51
  end
52
52
 
53
- it "includes screw.css" do
54
- @html.should =~ %r{href="/screw.css"}
53
+ it "includes shenandoah.css" do
54
+ @html.should =~ %r{href="/shenandoah.css"}
55
55
  end
56
56
  end
57
57
 
@@ -70,11 +70,11 @@ describe "shenandoah generator" do
70
70
  File.read(@path).should =~ %r{src="/shenandoah/browser-runner.js"}
71
71
  end
72
72
 
73
- it "links to the served screw.css" do
74
- File.read(@path).should =~ %r{href="/screw.css"}
73
+ it "links to the served shenandoah.css" do
74
+ File.read(@path).should =~ %r{href="/shenandoah.css"}
75
75
  end
76
76
  end
77
77
  end
78
78
  end
79
79
  end
80
- end
80
+ end
@@ -59,8 +59,13 @@ describe Shenandoah::Server do
59
59
  last_response.body.should =~ %r{function require_main\(}
60
60
  end
61
61
 
62
+ it "includes the multirunner code for a single test" do
63
+ last_response.body.should =~ %r{window.parent.jQuery\('iframe'\)}
64
+ end
65
+
62
66
  it "was last modified at the latest date of the combined files" do
63
67
  maxTime = Dir["#{File.dirname(__FILE__)}/../../lib/shenandoah/javascript/{browser,common}/*.js"].
68
+ reject { |filename| filename =~ /multirunner.js$/ || filename =~ /parsequery/i || filename =~ /index.js$/ }.
64
69
  collect { |filename| File.stat(filename).mtime }.max
65
70
  Time.httpdate(last_response.headers['Last-Modified']).should == maxTime
66
71
  end
@@ -114,37 +119,44 @@ describe Shenandoah::Server do
114
119
  end
115
120
  end
116
121
 
117
- describe "/screw.css" do
122
+ describe "/shenandoah.css" do
118
123
  describe "by default" do
119
124
  before do
120
- get '/screw.css'
121
- @included = "#{File.dirname(__FILE__)}/../../lib/shenandoah/css/screw.css"
125
+ get '/shenandoah.css'
126
+ @sass = File.expand_path(
127
+ "lib/shenandoah/css/shenandoah.sass", File.dirname(__FILE__) + "/../..")
122
128
  end
123
129
 
124
130
  it "is available" do
125
131
  last_response.should be_ok
126
132
  end
127
133
 
128
- it "is the included version" do
129
- last_response.body.should == File.read(@included)
134
+ it "is the CSS version of the included shenandoah.sass" do
135
+ last_response.body.should include('#frames iframe {')
136
+ last_response.body.should include('.describes .describe {')
137
+ end
138
+
139
+ it "includes the compass reset styles directly" do
140
+ last_response.body.should_not include("@import url(compass/reset.css)")
141
+ last_response.body.should include("table, caption, tbody, tfoot, thead, tr, th, td")
130
142
  end
131
143
 
132
144
  it "has the correct last modified version" do
133
145
  Time.httpdate(last_response.headers['Last-Modified']).should ==
134
- File.stat(@included).mtime
146
+ File.stat(@sass).mtime
135
147
  end
136
148
 
137
149
  it "is CSS" do
138
- last_response.headers['Content-Type'].should == 'text/css'
150
+ last_response.content_type.should == 'text/css'
139
151
  end
140
152
  end
141
153
 
142
- describe "when overridden" do
154
+ describe "when overridden with CSS" do
143
155
  before do
144
156
  app.set :locator,
145
157
  Shenandoah::DefaultLocator.new(:spec_path => self.tmpdir)
146
- tmpfile "screw.css", ".passed { color: blue }"
147
- get '/screw.css'
158
+ tmpfile "shenandoah.css", ".passed { color: blue }"
159
+ get '/shenandoah.css'
148
160
  end
149
161
 
150
162
  it "is available" do
@@ -157,13 +169,57 @@ describe Shenandoah::Server do
157
169
 
158
170
  it "has the correct last modified version" do
159
171
  Time.httpdate(last_response.headers['Last-Modified']).should ==
160
- File.stat("#{self.tmpdir}/screw.css").mtime
172
+ File.stat("#{self.tmpdir}/shenandoah.css").mtime
161
173
  end
162
174
 
163
175
  it "is CSS" do
164
- last_response.headers['Content-Type'].should == 'text/css'
176
+ last_response.content_type.should == 'text/css'
165
177
  end
166
178
  end
179
+
180
+ describe "when overridden with Sass" do
181
+ before do
182
+ app.set :locator,
183
+ Shenandoah::DefaultLocator.new(:spec_path => self.tmpdir)
184
+ tmpfile "shenandoah.sass", ".passed\n color: blue"
185
+ get '/shenandoah.css'
186
+ end
187
+
188
+ it "is available" do
189
+ last_response.should be_ok
190
+ end
191
+
192
+ it "is the version from the spec dir" do
193
+ last_response.body.should == ".passed {\n color: blue; }\n"
194
+ end
195
+
196
+ it "has the correct last modified version" do
197
+ Time.httpdate(last_response.headers['Last-Modified']).should ==
198
+ File.stat("#{self.tmpdir}/shenandoah.sass").mtime
199
+ end
200
+
201
+ it "is CSS" do
202
+ last_response.content_type.should == 'text/css'
203
+ end
204
+ end
205
+ end
206
+
207
+ describe "/screw.css" do
208
+ before do
209
+ get '/screw.css'
210
+ end
211
+
212
+ it "redirects" do
213
+ last_response.status.should == 301
214
+ end
215
+
216
+ it "points to /shenandoah.css" do
217
+ last_response['Location'].should == '/shenandoah.css'
218
+ end
219
+
220
+ it "includes a deprecation note" do
221
+ last_response.body.should == "This URI is deprecated. Use <a href='/shenandoah.css'>/shenandoah.css</a>."
222
+ end
167
223
  end
168
224
 
169
225
  describe "/" do
@@ -173,6 +229,7 @@ describe Shenandoah::Server do
173
229
  app.set :locator,
174
230
  Shenandoah::DefaultLocator.new(:spec_path => File.join(self.tmpdir, 'spec'))
175
231
  tmpfile "spec/common_spec.js", "DC"
232
+ tmpfile "spec/application_spec.js", "DC"
176
233
  tmpfile "spec/some/thing_spec.js", "DC"
177
234
  get "/"
178
235
  end
@@ -202,15 +259,99 @@ describe Shenandoah::Server do
202
259
  end
203
260
 
204
261
  it "includes lists of links" do
205
- last_response.body.should have_tag("ul a", :count => 2)
262
+ last_response.body.should have_tag("ul a", :count => 3)
206
263
  end
207
264
 
208
265
  it "includes a link to a spec in the root" do
209
- last_response.body.should have_tag("a[@href='spec/common.html']", "common")
266
+ last_response.body.should have_tag("a[@href='/spec/common.html']", "common")
210
267
  end
211
268
 
212
269
  it "includes link to a spec in a subdirectory" do
213
- last_response.body.should have_tag("a[@href='spec/some/thing.html']", "thing")
270
+ last_response.body.should have_tag("a[@href='/spec/some/thing.html']", "thing")
271
+ end
272
+
273
+ it "includes a checkbox for a spec in the root" do
274
+ last_response.body.should have_tag("input[@value='/spec/common.html']")
275
+ end
276
+
277
+ it "includes a checkbox for a spec in a subdirectory" do
278
+ last_response.body.should have_tag("input[@value='/spec/some/thing.html']")
279
+ end
280
+
281
+ it "includes a form to run the multirunner" do
282
+ last_response.body.should have_tag("form[@action='/multirunner']")
283
+ end
284
+
285
+ it "includes a submit button for the multirunner form" do
286
+ last_response.body.should have_tag("input[@type='submit']")
287
+ end
288
+ end
289
+
290
+ describe "/multirunner" do
291
+ include RspecHpricotMatchers
292
+
293
+ before do
294
+ get "/multirunner?spec=/spec/common.html"
295
+ end
296
+
297
+ it "is available" do
298
+ last_response.should be_ok
299
+ end
300
+
301
+ it "is html" do
302
+ last_response.content_type.should == 'text/html'
303
+ end
304
+
305
+ it "includes the multirunner script" do
306
+ last_response.body.should have_tag("script[@src='/shenandoah/multirunner.js']")
307
+ end
308
+
309
+ it "includes the container" do
310
+ last_response.body.should have_tag("div#runner")
311
+ end
312
+ end
313
+
314
+ describe "/shenandoah/multirunner.js" do
315
+ before do
316
+ get '/shenandoah/multirunner.js'
317
+ end
318
+
319
+ it "is available" do
320
+ last_response.should be_ok
321
+ end
322
+
323
+ it "is javascript" do
324
+ last_response.content_type.should == 'text/javascript'
325
+ end
326
+
327
+ it "contains the multirunner script" do
328
+ last_response.body.should =~ /shenandoah.Multirunner/
329
+ end
330
+
331
+ it "includes jQuery" do
332
+ last_response.body.should =~ %r{\* jQuery JavaScript Library v1\.3\.2}
333
+ end
334
+
335
+ it "includes parseQuery" do
336
+ last_response.body.should =~ %r{jQuery.parseQuery =}
337
+ end
338
+ end
339
+
340
+ describe "/js/common/jquery-1.3.2.js" do
341
+ before do
342
+ get '/js/common/jquery-1.3.2.js'
343
+ end
344
+
345
+ it "is available" do
346
+ last_response.should be_ok
347
+ end
348
+
349
+ it "is javascript" do
350
+ last_response.content_type.should == 'application/javascript'
351
+ end
352
+
353
+ it "includes jQuery" do
354
+ last_response.body.should =~ %r{\* jQuery JavaScript Library v1\.3\.2}
214
355
  end
215
356
  end
216
357
  end