rspec-jasmine 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +43 -0
- data/Rakefile +2 -0
- data/example/.rspec +1 -0
- data/example/Gemfile +7 -0
- data/example/README.md +18 -0
- data/example/Rakefile +18 -0
- data/example/config.ru +5 -0
- data/example/public/lib/jasmine-1.2.0/MIT.LICENSE +20 -0
- data/example/public/lib/jasmine-1.2.0/jasmine-html.js +616 -0
- data/example/public/lib/jasmine-1.2.0/jasmine.css +81 -0
- data/example/public/lib/jasmine-1.2.0/jasmine.js +2529 -0
- data/example/public/spec/PlayerSpec.js +58 -0
- data/example/public/spec/SpecHelper.js +9 -0
- data/example/public/src/Player.js +22 -0
- data/example/public/src/Song.js +7 -0
- data/example/public/tests.html +61 -0
- data/example/spec/jasmine.rb +13 -0
- data/example/spec/spec_helper.rb +6 -0
- data/lib/rspec-jasmine.rb +2 -0
- data/lib/rspec/jasmine.rb +9 -0
- data/lib/rspec/jasmine/example.rb +46 -0
- data/lib/rspec/jasmine/example_result.rb +45 -0
- data/lib/rspec/jasmine/selenium_driver.rb +56 -0
- data/lib/rspec/jasmine/spec_builder.rb +130 -0
- data/lib/rspec/jasmine/spec_runner.rb +44 -0
- data/lib/rspec/jasmine/version.rb +5 -0
- data/rspec-jasmine.gemspec +25 -0
- data/spec/.gitkeep +0 -0
- metadata +142 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
describe("Player", function() {
|
2
|
+
var player;
|
3
|
+
var song;
|
4
|
+
|
5
|
+
beforeEach(function() {
|
6
|
+
player = new Player();
|
7
|
+
song = new Song();
|
8
|
+
});
|
9
|
+
|
10
|
+
it("should be able to play a Song", function() {
|
11
|
+
player.play(song);
|
12
|
+
expect(player.currentlyPlayingSong).toEqual(song);
|
13
|
+
|
14
|
+
//demonstrates use of custom matcher
|
15
|
+
expect(player).toBePlaying(song);
|
16
|
+
});
|
17
|
+
|
18
|
+
describe("when song has been paused", function() {
|
19
|
+
beforeEach(function() {
|
20
|
+
player.play(song);
|
21
|
+
player.pause();
|
22
|
+
});
|
23
|
+
|
24
|
+
it("should indicate that the song is currently paused", function() {
|
25
|
+
expect(player.isPlaying).toBeFalsy();
|
26
|
+
|
27
|
+
// demonstrates use of 'not' with a custom matcher
|
28
|
+
expect(player).not.toBePlaying(song);
|
29
|
+
});
|
30
|
+
|
31
|
+
it("should be possible to resume", function() {
|
32
|
+
player.resume();
|
33
|
+
expect(player.isPlaying).toBeTruthy();
|
34
|
+
expect(player.currentlyPlayingSong).toEqual(song);
|
35
|
+
});
|
36
|
+
});
|
37
|
+
|
38
|
+
// demonstrates use of spies to intercept and test method calls
|
39
|
+
it("tells the current song if the user has made it a favorite", function() {
|
40
|
+
spyOn(song, 'persistFavoriteStatus');
|
41
|
+
|
42
|
+
player.play(song);
|
43
|
+
player.makeFavorite();
|
44
|
+
|
45
|
+
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(false);
|
46
|
+
});
|
47
|
+
|
48
|
+
//demonstrates use of expected exceptions
|
49
|
+
describe("#resume", function() {
|
50
|
+
it("should throw an exception if song is already playing", function() {
|
51
|
+
player.play(song);
|
52
|
+
|
53
|
+
expect(function() {
|
54
|
+
player.resume();
|
55
|
+
}).toThrow("song is already playing");
|
56
|
+
});
|
57
|
+
});
|
58
|
+
});
|
@@ -0,0 +1,22 @@
|
|
1
|
+
function Player() {
|
2
|
+
}
|
3
|
+
Player.prototype.play = function(song) {
|
4
|
+
this.currentlyPlayingSong = song;
|
5
|
+
this.isPlaying = true;
|
6
|
+
};
|
7
|
+
|
8
|
+
Player.prototype.pause = function() {
|
9
|
+
this.isPlaying = false;
|
10
|
+
};
|
11
|
+
|
12
|
+
Player.prototype.resume = function() {
|
13
|
+
if (this.isPlaying) {
|
14
|
+
throw new Error("song is already playing");
|
15
|
+
}
|
16
|
+
|
17
|
+
this.isPlaying = true;
|
18
|
+
};
|
19
|
+
|
20
|
+
Player.prototype.makeFavorite = function() {
|
21
|
+
this.currentlyPlayingSong.persistFavoriteStatus(true);
|
22
|
+
};
|
@@ -0,0 +1,61 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Jasmine Spec Runner</title>
|
5
|
+
|
6
|
+
<link rel="shortcut icon" type="image/png" href="/lib/jasmine-1.2.0/jasmine_favicon.png">
|
7
|
+
<link rel="stylesheet" type="text/css" href="/lib/jasmine-1.2.0/jasmine.css">
|
8
|
+
|
9
|
+
<!-- Jasmine lib files -->
|
10
|
+
<script type="text/javascript" src="/lib/jasmine-1.2.0/jasmine.js"></script>
|
11
|
+
<script type="text/javascript" src="/lib/jasmine-1.2.0/jasmine-html.js"></script>
|
12
|
+
|
13
|
+
<!-- Source files -->
|
14
|
+
<script type="text/javascript" src="src/Player.js"></script>
|
15
|
+
<script type="text/javascript" src="src/Song.js"></script>
|
16
|
+
|
17
|
+
<!--
|
18
|
+
Your spec files - can be all the scripts separately, or some
|
19
|
+
nicely combined together with asset pipeline, etc.
|
20
|
+
-->
|
21
|
+
<script type="text/javascript" src="spec/SpecHelper.js"></script>
|
22
|
+
<script type="text/javascript" src="spec/PlayerSpec.js"></script>
|
23
|
+
|
24
|
+
<!-- Jasmine suite runner -->
|
25
|
+
<script type="text/javascript">
|
26
|
+
|
27
|
+
var jsApiReporter;
|
28
|
+
|
29
|
+
(function() {
|
30
|
+
var jasmineEnv = jasmine.getEnv();
|
31
|
+
jasmineEnv.updateInterval = 1000;
|
32
|
+
|
33
|
+
jsApiReporter = new jasmine.JsApiReporter();
|
34
|
+
var htmlReporter = new jasmine.HtmlReporter();
|
35
|
+
|
36
|
+
jasmineEnv.addReporter(jsApiReporter);
|
37
|
+
jasmineEnv.addReporter(htmlReporter);
|
38
|
+
|
39
|
+
jasmineEnv.specFilter = function(spec) {
|
40
|
+
return htmlReporter.specFilter(spec);
|
41
|
+
};
|
42
|
+
|
43
|
+
var currentWindowOnload = window.onload;
|
44
|
+
|
45
|
+
window.onload = function() {
|
46
|
+
if (currentWindowOnload) {
|
47
|
+
currentWindowOnload();
|
48
|
+
}
|
49
|
+
execJasmine();
|
50
|
+
};
|
51
|
+
|
52
|
+
function execJasmine() {
|
53
|
+
jasmineEnv.execute();
|
54
|
+
}
|
55
|
+
})();
|
56
|
+
|
57
|
+
</script>
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
</body>
|
61
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
require 'rspec/jasmine'
|
3
|
+
|
4
|
+
config_ru = File.expand_path('../../config.ru', __FILE__)
|
5
|
+
app, _ = Rack::Builder.parse_file(config_ru)
|
6
|
+
|
7
|
+
selected_suites = ENV['SUITES'].split(':') if !!ENV['SUITES']
|
8
|
+
|
9
|
+
RSpec::Jasmine::SpecRunner.run(self,
|
10
|
+
:app => app,
|
11
|
+
:port => 3001,
|
12
|
+
:suites => selected_suites || %w{/tests.html}
|
13
|
+
)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rspec/core/example'
|
2
|
+
require 'rspec/jasmine/example_result'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module RSpec
|
6
|
+
module Jasmine
|
7
|
+
class Example < RSpec::Core::Example
|
8
|
+
def run(example_group_instance, reporter)
|
9
|
+
@example_group_instance = example_group_instance
|
10
|
+
@example_group_instance.example = self
|
11
|
+
|
12
|
+
start(reporter)
|
13
|
+
|
14
|
+
begin
|
15
|
+
unless pending
|
16
|
+
begin
|
17
|
+
@example_group_instance.instance_eval(&@example_block)
|
18
|
+
@result = @example_group_instance.instance_variable_get('@result')
|
19
|
+
@result.screem! if @result
|
20
|
+
rescue RSpec::Core::Pending::PendingDeclaredInExample => e
|
21
|
+
@pending_declared_in_example = e.message
|
22
|
+
rescue Exception => e
|
23
|
+
set_exception(e)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
rescue Exception => e
|
27
|
+
set_exception(e)
|
28
|
+
ensure
|
29
|
+
@example_group_instance.instance_variables.each do |ivar|
|
30
|
+
@example_group_instance.instance_variable_set(ivar, nil)
|
31
|
+
end
|
32
|
+
@example_group_instance = nil
|
33
|
+
|
34
|
+
begin
|
35
|
+
assign_generated_description
|
36
|
+
rescue Exception => e
|
37
|
+
set_exception(e, "while assigning the example description")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
@result.merge_backtrace_with!(exception) if exception
|
42
|
+
finish(reporter)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Jasmine
|
3
|
+
class ExampleResult < Hash
|
4
|
+
def initialize(results)
|
5
|
+
super()
|
6
|
+
merge!(results)
|
7
|
+
end
|
8
|
+
|
9
|
+
def failed?
|
10
|
+
self['result'] == 'failed'
|
11
|
+
end
|
12
|
+
|
13
|
+
def failure
|
14
|
+
@failed_message ||= self['messages'].to_a.find { |m| m['passed'] == false }
|
15
|
+
end
|
16
|
+
|
17
|
+
def error_message
|
18
|
+
failure['message'].to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge_backtrace_with!(e)
|
22
|
+
e.instance_variable_set('@stack', self.backtrace)
|
23
|
+
|
24
|
+
class << e
|
25
|
+
def backtrace
|
26
|
+
return @stack
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def backtrace
|
32
|
+
trace = failure['trace'] || {}
|
33
|
+
trace['stack'].to_s.split(/$/).map(&:strip).delete_if do |line|
|
34
|
+
line =~ /\/lib\/jasmine-\d+\.\d+\.\d+\/jasmine\.js\:\d+/ || line.strip.empty?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def screem!
|
39
|
+
if failed?
|
40
|
+
raise RSpec::Expectations::ExpectationNotMetError.new(error_message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'selenium-webdriver'
|
2
|
+
require 'enumerator'
|
3
|
+
|
4
|
+
module RSpec
|
5
|
+
module Jasmine
|
6
|
+
class SeleniumDriver
|
7
|
+
attr_reader :options, :browser, :address
|
8
|
+
|
9
|
+
def initialize(browser, options = {})
|
10
|
+
@options = options
|
11
|
+
@browser = browser
|
12
|
+
end
|
13
|
+
|
14
|
+
def selenium_server
|
15
|
+
@selenium_server = if ENV['SELENIUM_SERVER']
|
16
|
+
ENV['SELENIUM_SERVER']
|
17
|
+
elsif ENV['SELENIUM_SERVER_PORT']
|
18
|
+
"http://localhost:#{ENV['SELENIUM_SERVER_PORT']}/wd/hub"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def driver
|
23
|
+
@driver ||= if selenium_server
|
24
|
+
Selenium::WebDriver.for :remote, :url => selenium_server, :desired_capabilities => browser.to_sym
|
25
|
+
else
|
26
|
+
Selenium::WebDriver.for browser.to_sym, options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def tests_finished?
|
31
|
+
x("return jsApiReporter.finished") == 'true'
|
32
|
+
end
|
33
|
+
|
34
|
+
def connect(address)
|
35
|
+
driver.navigate.to(address)
|
36
|
+
end
|
37
|
+
|
38
|
+
def disconnect
|
39
|
+
driver.quit
|
40
|
+
end
|
41
|
+
|
42
|
+
def eval_js(script)
|
43
|
+
result = x(script)
|
44
|
+
JSON.parse("{\"result\":#{result}}", :max_nesting => false)["result"]
|
45
|
+
end
|
46
|
+
|
47
|
+
def json_generate(obj)
|
48
|
+
JSON.generate(obj)
|
49
|
+
end
|
50
|
+
|
51
|
+
def x(script)
|
52
|
+
driver.execute_script(script)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'rspec/jasmine/selenium_driver'
|
2
|
+
require 'rspec/jasmine/example'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module RSpec
|
6
|
+
module Jasmine
|
7
|
+
class SpecBuilder
|
8
|
+
def initialize(world, config = {})
|
9
|
+
@world = world
|
10
|
+
@config = config
|
11
|
+
@running = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def suite
|
15
|
+
@config[:suite] || '/tests'
|
16
|
+
end
|
17
|
+
|
18
|
+
def browser
|
19
|
+
@config[:browser] || ENV["JASMINE_BROWSER"] || 'firefox'
|
20
|
+
end
|
21
|
+
|
22
|
+
def host
|
23
|
+
@config[:host] || ENV["JASMINE_HOST"] || 'localhost'
|
24
|
+
end
|
25
|
+
|
26
|
+
def port
|
27
|
+
@config[:port] || ENV["JASMINE_PORT"] || '5001'
|
28
|
+
end
|
29
|
+
|
30
|
+
def url
|
31
|
+
"http://#{host}:#{port}#{suite}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def start
|
35
|
+
@client = Jasmine::SeleniumDriver.new(browser)
|
36
|
+
@running = true
|
37
|
+
|
38
|
+
@client.connect(url)
|
39
|
+
|
40
|
+
puts "Running test suite with Jasmine against: #{url}"
|
41
|
+
|
42
|
+
load_suite_info!
|
43
|
+
generate_report!
|
44
|
+
wait_for_suites_to_finish
|
45
|
+
end
|
46
|
+
|
47
|
+
def stop
|
48
|
+
@client.disconnect if @running
|
49
|
+
@running = false
|
50
|
+
end
|
51
|
+
|
52
|
+
def wait_for_suites_to_finish
|
53
|
+
sleep 0.1 until eval_js('return jsApiReporter.finished')
|
54
|
+
end
|
55
|
+
|
56
|
+
def eval_js(script)
|
57
|
+
@client.eval_js(script)
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_suite_info!
|
61
|
+
started = Time.now
|
62
|
+
|
63
|
+
while !eval_js('return jsApiReporter && jsApiReporter.started') do
|
64
|
+
raise "couldn't connect to Jasmine after 60 seconds" if (started + 60 < Time.now)
|
65
|
+
sleep 0.1
|
66
|
+
end
|
67
|
+
|
68
|
+
@spec_ids = []
|
69
|
+
@spec_results = nil
|
70
|
+
@test_suites = eval_js("var result = jsApiReporter.suites(); return JSON.stringify(result)")
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_report!
|
74
|
+
@test_suites.to_a.each do |suite|
|
75
|
+
declare_suite!(@world, suite)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def declare_suite!(parent, suite)
|
80
|
+
me = self
|
81
|
+
|
82
|
+
parent.describe suite['name'] do
|
83
|
+
suite['children'].each do |suite_or_spec|
|
84
|
+
case suite_or_spec["type"]
|
85
|
+
when "suite"
|
86
|
+
me.declare_suite!(self, suite_or_spec)
|
87
|
+
when "spec"
|
88
|
+
me.declare_spec!(self, suite_or_spec)
|
89
|
+
else
|
90
|
+
raise "unknown type #{type} for #{suite_or_spec.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def declare_spec!(parent, spec)
|
97
|
+
me = self
|
98
|
+
|
99
|
+
spec_id = spec['id']
|
100
|
+
@spec_ids << spec_id
|
101
|
+
|
102
|
+
meta = parent.build_metadata_hash_from([])
|
103
|
+
block = proc do
|
104
|
+
@result = Jasmine::ExampleResult.new(me.results_for(spec_id))
|
105
|
+
Jasmine.failed = true if @result.failed?
|
106
|
+
end
|
107
|
+
|
108
|
+
parent.examples << Jasmine::Example.new(parent, spec['name'], meta, block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def json_generate(obj)
|
112
|
+
@client.json_generate(obj)
|
113
|
+
end
|
114
|
+
|
115
|
+
def results_for(spec_id)
|
116
|
+
@spec_results ||= load_results
|
117
|
+
@spec_results[spec_id.to_s]
|
118
|
+
end
|
119
|
+
|
120
|
+
def load_results
|
121
|
+
@spec_ids.each_slice(50).inject({}) do |results, slice|
|
122
|
+
results.merge(eval_js(<<-JS))
|
123
|
+
var result = jsApiReporter.resultsForSpecs(#{json_generate(slice)});
|
124
|
+
return JSON.stringify(result);
|
125
|
+
JS
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|