spassky 0.1.0

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.
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