rspec-jasmine 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/.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
|