spassky 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +84 -0
  5. data/LICENSE +13 -0
  6. data/README.md +47 -0
  7. data/Rakefile +5 -0
  8. data/bin/spassky +8 -0
  9. data/bin/spassky-server +9 -0
  10. data/config.ru +6 -0
  11. data/features/connection.feature +9 -0
  12. data/features/device_timeout.feature +31 -0
  13. data/features/run_html_tests.feature +65 -0
  14. data/features/run_qunit_tests.feature +45 -0
  15. data/features/server.feature +11 -0
  16. data/features/step_definitions/steps.rb +66 -0
  17. data/features/support/env.rb +31 -0
  18. data/features/support/fixtures/qunit.js +1512 -0
  19. data/lib/spassky/client/cli.rb +12 -0
  20. data/lib/spassky/client/directory_reader.rb +14 -0
  21. data/lib/spassky/client/pusher.rb +36 -0
  22. data/lib/spassky/client/test_runner.rb +44 -0
  23. data/lib/spassky/client/writer.rb +31 -0
  24. data/lib/spassky/client.rb +1 -0
  25. data/lib/spassky/server/app.rb +76 -0
  26. data/lib/spassky/server/assert.js +5 -0
  27. data/lib/spassky/server/device_list.rb +18 -0
  28. data/lib/spassky/server/html_test.rb +27 -0
  29. data/lib/spassky/server/random_string_generator.rb +7 -0
  30. data/lib/spassky/server/test_run.rb +64 -0
  31. data/lib/spassky/server.rb +1 -0
  32. data/lib/spassky/test_result.rb +110 -0
  33. data/lib/spassky/version.rb +3 -0
  34. data/lib/spassky.rb +9 -0
  35. data/spassky.gemspec +31 -0
  36. data/spec/spassky/client/cli_spec.rb +48 -0
  37. data/spec/spassky/client/directory_reader_spec.rb +35 -0
  38. data/spec/spassky/client/pusher_spec.rb +72 -0
  39. data/spec/spassky/client/test_runner_spec.rb +130 -0
  40. data/spec/spassky/client/writer_spec.rb +31 -0
  41. data/spec/spassky/server/app_spec.rb +177 -0
  42. data/spec/spassky/server/device_list_spec.rb +32 -0
  43. data/spec/spassky/server/random_string_generator_spec.rb +13 -0
  44. data/spec/spassky/server/test_run_spec.rb +98 -0
  45. data/spec/spassky/test_result_spec.rb +116 -0
  46. data/spec/spec_helper.rb +3 -0
  47. metadata +215 -0
@@ -0,0 +1,12 @@
1
+ require 'spassky/client/test_runner'
2
+ require 'spassky/client/pusher'
3
+ require 'spassky/client/directory_reader'
4
+
5
+ module Spassky::Client
6
+ class Cli
7
+ def self.run(argv)
8
+ writer = argv.include?('--colour') ? ColouredWriter : DefaultWriter
9
+ TestRunner.new(Pusher.new(argv[0]), writer.new(STDOUT), DirectoryReader.new).run_tests(argv[1])
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Spassky::Client
2
+ class DirectoryReader
3
+ def read_files(pattern)
4
+ if File.file? pattern
5
+ { pattern => File.read(pattern) }
6
+ elsif File.directory? pattern
7
+ Dir.glob(pattern + "/*").inject({}) do |hash, path|
8
+ hash[File.basename(path)] = File.read(path)
9
+ hash
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ require 'rest-client'
2
+ require 'spassky/test_result'
3
+
4
+ module Spassky::Client
5
+ class Pusher
6
+ def initialize(server_url, sleeper=Kernel)
7
+ @server_url = server_url
8
+ @sleeper = sleeper
9
+ end
10
+
11
+ def test_runs_url
12
+ test_runs_url = @server_url.gsub(/\/$/, "") + "/test_runs"
13
+ end
14
+
15
+ def push(options)
16
+ location = post_test(options)
17
+ result = nil
18
+ begin
19
+ result = Spassky::TestResult.from_json(RestClient.get(location))
20
+ yield result
21
+ @sleeper.sleep 0.4 if result.status == 'in progress'
22
+ end while result.status == 'in progress'
23
+ end
24
+
25
+ private
26
+
27
+ def post_test(options)
28
+ location = nil
29
+ RestClient.post(test_runs_url, options) do |response, request, result|
30
+ location = response.headers[:location]
31
+ end
32
+ raise "Expected #{test_runs_url} to respond with 302" unless location
33
+ location
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ require 'spassky/client/writer'
2
+
3
+ module Spassky::Client
4
+ class TestRunner
5
+ def initialize(pusher, writer, directory_reader)
6
+ @pusher = pusher
7
+ @writer = writer
8
+ @directory_reader = directory_reader
9
+ end
10
+
11
+ def run_tests(pattern)
12
+ previous_test_result = nil
13
+ test_name = File.basename(pattern)
14
+ @pusher.push(:name => test_name, :contents => @directory_reader.read_files(pattern).to_json) do |result|
15
+ handle_test_result(previous_test_result, result)
16
+ previous_test_result = result
17
+ end
18
+ end
19
+
20
+ def handle_test_result(previous_test_result, test_result)
21
+ write_in_progress_status previous_test_result, test_result
22
+ unless test_result.status == "in progress"
23
+ write(test_result.status, test_result.summary)
24
+ end
25
+ write_exit_code(test_result)
26
+ end
27
+
28
+ def write_in_progress_status previous_test_result, test_result
29
+ test_result.completed_since(previous_test_result).each do |device_test_status|
30
+ write(device_test_status.status, "#{device_test_status.status.upcase} #{device_test_status.test_name} on #{device_test_status.user_agent}")
31
+ end
32
+ end
33
+
34
+ def write status, message
35
+ method = status == 'pass' ? :write_passing : :write_failing
36
+ @writer.send(method, message)
37
+ end
38
+
39
+ def write_exit_code(result)
40
+ Kernel.exit(1) if result.status == 'fail'
41
+ Kernel.exit(2) if result.status == 'timed out'
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ require 'rainbow'
2
+
3
+ module Spassky::Client
4
+ class DefaultWriter
5
+ def initialize(output)
6
+ @output = output
7
+ end
8
+
9
+ def write_passing(text)
10
+ @output.puts text
11
+ end
12
+
13
+ def write_failing(text)
14
+ @output.puts text
15
+ end
16
+ end
17
+
18
+ class ColouredWriter
19
+ def initialize(output)
20
+ @output = output
21
+ end
22
+
23
+ def write_passing(text)
24
+ @output.puts text.color(:green)
25
+ end
26
+
27
+ def write_failing(text)
28
+ @output.puts text.color(:red)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1 @@
1
+ require 'spassky/client/cli'
@@ -0,0 +1,76 @@
1
+ require 'sinatra/base'
2
+ require 'spassky/server/random_string_generator'
3
+ require 'spassky/server/test_run'
4
+ require 'spassky/server/device_list'
5
+ require 'spassky/server/html_test'
6
+
7
+ module Spassky::Server
8
+ class App < Sinatra::Base
9
+ def initialize(device_list=DeviceList.new)
10
+ @device_list = device_list
11
+ super()
12
+ end
13
+
14
+ get "/devices/clear" do
15
+ @device_list.clear
16
+ end
17
+
18
+ get '/device/connect' do
19
+ redirect idle_url
20
+ end
21
+
22
+ get '/device/idle/:random' do
23
+ test_run = TestRun.find_next_to_run_for_user_agent(request.user_agent)
24
+ @device_list.update_last_connected(request.user_agent)
25
+ if test_run
26
+ redirect_to_run_tests(test_run)
27
+ else
28
+ idle_page
29
+ end
30
+ end
31
+
32
+ post '/test_runs' do
33
+ run = TestRun.create({
34
+ :name => params[:name],
35
+ :contents => JSON.parse(params[:contents]),
36
+ :devices => @device_list.recently_connected_devices
37
+ })
38
+ redirect "/test_runs/#{run.id}"
39
+ end
40
+
41
+ get '/test_runs/:id' do
42
+ run = TestRun.find(params[:id])
43
+ run.update_connected_devices(@device_list.recently_connected_devices)
44
+ run.result.to_json
45
+ end
46
+
47
+ get '/test_runs/:id/run/:random/assert' do
48
+ TestRun.find(params[:id]).save_results_for_user_agent(
49
+ :user_agent => request.user_agent,
50
+ :status => params[:status]
51
+ )
52
+ end
53
+
54
+ get '/test_runs/:id/run/:random/:file_name' do
55
+ test_run = TestRun.find(params[:id])
56
+ test_name = params[:file_name]
57
+ HtmlTest.new(test_run.contents, idle_url, 1).get_file(test_name)
58
+ end
59
+
60
+ private
61
+
62
+ def redirect_to_run_tests(test_run)
63
+ redirect "/test_runs/#{test_run.id}/run/#{RandomStringGenerator.random_string}/#{test_run.name}"
64
+ end
65
+
66
+ def idle_url
67
+ "/device/idle/#{RandomStringGenerator.random_string}"
68
+ end
69
+
70
+ def idle_page
71
+ "<html><head><meta http-equiv=\"refresh\" content=\"1; url='#{idle_url}'\"></head>" +
72
+ "<body>Idle #{RandomStringGenerator.random_string}</body>" +
73
+ "</html>"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ function assert(status, message) {
2
+ var img = document.createElement('img');
3
+ img.setAttribute('src', 'assert?status=' + (status ? 'pass' : 'fail') + '&message=' + escape(message));
4
+ document.body.appendChild(img);
5
+ }
@@ -0,0 +1,18 @@
1
+ module Spassky::Server
2
+ class DeviceList
3
+ def update_last_connected user_agent
4
+ @devices_and_time_last_connected ||= {}
5
+ @devices_and_time_last_connected[user_agent] = Time.now
6
+ end
7
+
8
+ def recently_connected_devices
9
+ @devices_and_time_last_connected.keys.select do |user_agent|
10
+ Time.now.to_f - @devices_and_time_last_connected[user_agent].to_f < 3
11
+ end
12
+ end
13
+
14
+ def clear
15
+ @devices_and_time_last_connected = {}
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ module Spassky::Server
2
+ class HtmlTest
3
+ def initialize(contents, url, seconds)
4
+ @contents = contents
5
+ @meta_refresh_tag = "<meta http-equiv=\"refresh\" content=\"#{seconds}; url='#{url}'\">"
6
+ end
7
+
8
+ def get_file(name)
9
+ file_contents = @contents[name]
10
+ unless file_contents
11
+ html_file = @contents.keys.find {|key| key.end_with?(".html")}
12
+ file_contents = @contents[html_file]
13
+ end
14
+ file_contents.gsub('</head>', assert_js_script_tag + @meta_refresh_tag + '</head>')
15
+ end
16
+
17
+ private
18
+
19
+ def assert_js_script_tag
20
+ "<script type=\"text/javascript\">#{assert_js_script}</script>"
21
+ end
22
+
23
+ def assert_js_script
24
+ File.read(File.join(File.dirname(__FILE__), 'assert.js'))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module Spassky::Server
2
+ class RandomStringGenerator
3
+ def self.random_string
4
+ Time.now.to_i.to_s
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ require 'spassky/test_result'
2
+
3
+ module Spassky::Server
4
+ class TestRun
5
+ attr_accessor :name, :contents, :id
6
+
7
+ def initialize(options)
8
+ @name = options[:name]
9
+ @contents = options[:contents]
10
+ @status_by_user_agent = {}
11
+ (options[:devices] || []).each do |device|
12
+ @status_by_user_agent[device] = "in progress"
13
+ end
14
+ end
15
+
16
+ def run_by_user_agent?(user_agent)
17
+ @status_by_user_agent[user_agent] != "in progress"
18
+ end
19
+
20
+ def save_results_for_user_agent(options)
21
+ unless ['pass', 'fail'].include? options[:status]
22
+ raise "#{options[:status]} is not a valid status"
23
+ end
24
+ @status_by_user_agent[options[:user_agent]] = options[:status]
25
+ end
26
+
27
+ def update_connected_devices(user_agents)
28
+ @status_by_user_agent.each do |user_agent, status|
29
+ if !user_agents.include?(user_agent) && status == "in progress"
30
+ @status_by_user_agent[user_agent] = "timed out"
31
+ end
32
+ end
33
+ end
34
+
35
+ def result
36
+ Spassky::TestResult.new(@status_by_user_agent.map { |user_agent, status|
37
+ Spassky::DeviceTestStatus.new(user_agent, status, name)
38
+ })
39
+ end
40
+
41
+ def self.create(options)
42
+ new_test_run = TestRun.new(options)
43
+ new_test_run.id = test_runs.size
44
+ test_runs << new_test_run
45
+ new_test_run
46
+ end
47
+
48
+ def self.find(id)
49
+ test_runs.find { |test_run| test_run.id.to_s == id.to_s }
50
+ end
51
+
52
+ def self.find_next_to_run_for_user_agent(user_agent)
53
+ test_runs.find { |test_run| test_run.run_by_user_agent?(user_agent) == false }
54
+ end
55
+
56
+ def self.delete_all
57
+ @test_runs = []
58
+ end
59
+
60
+ def self.test_runs
61
+ @test_runs ||= []
62
+ end
63
+ end
64
+ end
@@ -0,0 +1 @@
1
+ require 'spassky/server/app'
@@ -0,0 +1,110 @@
1
+ require 'json'
2
+
3
+ module Spassky
4
+ class TestResult
5
+ attr_reader :device_statuses
6
+ def initialize device_statuses
7
+ @device_statuses = device_statuses
8
+ end
9
+
10
+ def status
11
+ statuses = @device_statuses.map { |s| s.status }.uniq
12
+ return "in progress" if statuses.include?("in progress") || statuses.size == 0
13
+ return "fail" if statuses.include?("fail")
14
+ return "timed out" if statuses.include?("timed out")
15
+ "pass"
16
+ end
17
+
18
+ def count_fails
19
+ @device_statuses.count { |s| s.status == "fail" }
20
+ end
21
+
22
+ def count_timeouts
23
+ @device_statuses.count { |s| s.status == "timed out" }
24
+ end
25
+
26
+ def completed_since(older_test_result)
27
+ if older_test_result.nil?
28
+ device_statuses.select { |s| s.completed? }
29
+ else
30
+ find_newly_completed_device_results(older_test_result)
31
+ end
32
+ end
33
+
34
+ def summary
35
+ result = "?"
36
+ count = @device_statuses.size
37
+ if count_timeouts > 0
38
+ result = "1 test timed out on #{count} device"
39
+ else
40
+ status = "passed"
41
+ fail_count = count_fails
42
+ if fail_count > 0
43
+ status = "failed"
44
+ count = fail_count
45
+ end
46
+ result = "1 test #{status} on #{count} device"
47
+ end
48
+ result << "s" if @device_statuses.size > 1
49
+ return result
50
+ end
51
+
52
+ def to_json
53
+ {
54
+ :status => "pass",
55
+ :device_statuses => @device_statuses.map do |status|
56
+ {
57
+ :user_agent => status.user_agent,
58
+ :status => status.status,
59
+ :test_name => status.test_name
60
+ }
61
+ end
62
+ }.to_json
63
+ end
64
+
65
+ def self.from_json json
66
+ parsed = JSON.parse(json)
67
+ test_result = TestResult.new(
68
+ parsed['device_statuses'].map do |t|
69
+ DeviceTestStatus.new(t["user_agent"], t["status"], t["test_name"])
70
+ end
71
+ )
72
+ end
73
+
74
+ private
75
+
76
+ def find_newly_completed_device_results(older_test_result)
77
+ completed = []
78
+ before_and_after(older_test_result) do |before, after|
79
+ if before.in_progress? && after.completed?
80
+ completed << after
81
+ end
82
+ end
83
+ completed
84
+ end
85
+
86
+ def before_and_after(older_test_result)
87
+ device_statuses.each_with_index do |s, i|
88
+ yield older_test_result.device_statuses[i], s
89
+ end
90
+ end
91
+ end
92
+
93
+ class DeviceTestStatus
94
+ attr_reader :user_agent, :status, :test_name
95
+
96
+ def initialize(user_agent, status, test_name)
97
+ @user_agent = user_agent
98
+ @status = status
99
+ @test_name = test_name
100
+ end
101
+
102
+ def in_progress?
103
+ @status == "in progress"
104
+ end
105
+
106
+ def completed?
107
+ @status != "in progress"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,3 @@
1
+ module Spassky
2
+ VERSION = '0.1.0'
3
+ end
data/lib/spassky.rb ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Spassky
5
+ module Server
6
+ end
7
+ module Client
8
+ end
9
+ end
data/spassky.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "spassky/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "spassky"
7
+ s.version = Spassky::VERSION
8
+ s.authors = ["Joshua Chisholm", "Andrew Vos", "Adrian Lewis"]
9
+ s.email = ["andrew.vos@gmail.com"]
10
+ s.homepage = "http://github.com/BBC/spassky"
11
+ s.summary = %q{}
12
+ s.description = %q{}
13
+
14
+ s.rubyforge_project = "spassky"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'rest-client'
22
+ s.add_dependency 'json'
23
+ s.add_dependency 'sinatra'
24
+ s.add_dependency 'rainbow'
25
+
26
+ s.add_development_dependency 'rake'
27
+ s.add_development_dependency 'rspec'
28
+ s.add_development_dependency 'cucumber'
29
+ s.add_development_dependency 'capybara'
30
+ s.add_development_dependency 'aruba'
31
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'spassky/client/cli'
3
+
4
+ module Spassky::Client
5
+ describe Cli do
6
+ let :pusher do
7
+ mock(:pusher)
8
+ end
9
+
10
+ let :runner do
11
+ mock(:runner, :run_tests => true)
12
+ end
13
+
14
+ before do
15
+ Pusher.stub!(:new).and_return(pusher)
16
+ TestRunner.stub!(:new).and_return(runner)
17
+ end
18
+
19
+ it "creates a pusher with the server url as the first argument" do
20
+ Pusher.should_receive(:new).with("server_name").and_return(pusher)
21
+ TestRunner.should_receive(:new).with(pusher, anything(), anything()).and_return(runner)
22
+ Cli::run(["server_name", "test_name"])
23
+ end
24
+
25
+ it "runs a single test with the name as the second argument" do
26
+ runner.should_receive(:run_tests).with("test_name")
27
+ Cli::run(["server_name", "test_name"])
28
+ end
29
+
30
+ context "without colour output option" do
31
+ it "creates a test runner with a default writer" do
32
+ default_writer = mock :default_writer
33
+ DefaultWriter.should_receive(:new).with(STDOUT).and_return(default_writer)
34
+ TestRunner.should_receive(:new).with(anything(), default_writer, anything())
35
+ Cli::run(["server_name", "test_name"])
36
+ end
37
+ end
38
+
39
+ context "with colour output option" do
40
+ it "creates a test runner with a colour writer" do
41
+ coloured_writer = mock :coloured_writer
42
+ ColouredWriter.should_receive(:new).with(STDOUT).and_return(coloured_writer)
43
+ TestRunner.should_receive(:new).with(anything(), coloured_writer, anything())
44
+ Cli::run(["server_name", "test_name", "--colour"])
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ require 'spassky/client/directory_reader'
2
+
3
+ module Spassky::Client
4
+ describe DirectoryReader do
5
+ context "when given a file name" do
6
+ it "returns a hash with one file" do
7
+ File.should_receive(:file?).and_return(true)
8
+ File.should_receive(:read).with("foo").and_return("content")
9
+ DirectoryReader.new.read_files("foo").should == {
10
+ "foo" => "content"
11
+ }
12
+ end
13
+ end
14
+ context "when given a directory name" do
15
+ it "globs for files in the specified directory" do
16
+ File.stub!(:file?).and_return(false)
17
+ File.stub!(:directory?).and_return(true)
18
+ Dir.should_receive(:glob).with("directory/*").and_return([])
19
+ DirectoryReader.new.read_files("directory")
20
+ end
21
+
22
+ it "returns a hash with all files in that directory" do
23
+ File.stub!(:file?).and_return(false)
24
+ File.stub!(:directory?).and_return(true)
25
+ Dir.stub!(:glob).and_return ["directory/file1", "directory/file2"]
26
+ File.stub!(:read).with("directory/file1").and_return("file 1 contents")
27
+ File.stub!(:read).with("directory/file2").and_return("file 2 contents")
28
+ DirectoryReader.new.read_files("directory").should == {
29
+ "file1" => "file 1 contents",
30
+ "file2" => "file 2 contents"
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'spassky/client/pusher'
3
+
4
+ module Spassky::Client
5
+ describe Pusher do
6
+ before do
7
+ @response = mock("response", :code => 302, :headers => { :location => "http://poll/me" })
8
+ @server_url = "http://foo/"
9
+ @sleeper = mock("sleeper")
10
+ @sleeper.stub!(:sleep)
11
+ @pusher = Pusher.new(@server_url, @sleeper)
12
+ RestClient.stub!(:post).with("http://foo/test_runs", "test contents"
13
+ ).and_yield(@response, nil, nil)
14
+ RestClient.stub!(:get).and_return(passed_status)
15
+ end
16
+
17
+ def in_progress_status
18
+ Spassky::TestResult.new([Spassky::DeviceTestStatus.new('agent', 'in progress', 'test')]).to_json
19
+ end
20
+
21
+ def passed_status
22
+ Spassky::TestResult.new([Spassky::DeviceTestStatus.new('agent', 'pass', 'test')]).to_json
23
+ end
24
+
25
+ def failed_status
26
+ Spassky::TestResult.new([Spassky::DeviceTestStatus.new('agent', 'fail', 'test')]).to_json
27
+ end
28
+
29
+ it "pushes a test to the server" do
30
+ RestClient.should_receive(:post).with("http://foo/test_runs", {:contents => 'test contents'}
31
+ ).and_yield(@response, nil, nil)
32
+ @pusher.push({:contents => "test contents"}) do |result|
33
+ end
34
+ end
35
+
36
+ it "fails nicely when the url does not redirect" do
37
+ @response = mock("response", :code => 200, :headers => { })
38
+ RestClient.stub!(:post).with("http://foo/test_runs", "test contents"
39
+ ).and_yield(@response, nil, nil)
40
+ lambda {
41
+ @pusher.push("test contents") do |result|
42
+ end
43
+ }.should raise_error("Expected http://foo/test_runs to respond with 302")
44
+ end
45
+
46
+ it "polls the URL returned until the test passes" do
47
+ RestClient.should_receive(:get).with("http://poll/me").and_return(in_progress_status, in_progress_status, in_progress_status, passed_status)
48
+ @pusher.push("test contents") do |result|
49
+ end
50
+ end
51
+
52
+ it "polls the URL returned until the test fails" do
53
+ RestClient.should_receive(:get).and_return(in_progress_status, in_progress_status, failed_status)
54
+ @pusher.push("test contents") {}
55
+ end
56
+
57
+ it "yields the outcome of the test to the block" do
58
+ RestClient.stub!(:get).and_return(in_progress_status, in_progress_status, passed_status)
59
+ yielded_results = []
60
+ @pusher.push("test contents") do |result|
61
+ yielded_results << result.to_json
62
+ end
63
+ yielded_results.should == [in_progress_status, in_progress_status, passed_status]
64
+ end
65
+
66
+ it "sleeps while looping during get requests" do
67
+ RestClient.stub!(:get).and_return(in_progress_status, in_progress_status, in_progress_status, passed_status)
68
+ @sleeper.should_receive(:sleep).with(0.4).exactly(3).times
69
+ @pusher.push("test contents") { |result| }
70
+ end
71
+ end
72
+ end