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,130 @@
1
+ require 'spec_helper'
2
+ require 'spassky/client/test_runner'
3
+
4
+ module Spassky::Client
5
+ describe TestRunner do
6
+ before do
7
+ @test_pusher = mock(:test_pusher)
8
+ @writer = mock(:writer)
9
+ @writer.stub!(:write_passing)
10
+ @writer.stub!(:write_failing)
11
+ Kernel.stub!(:exit)
12
+ @directory_reader = mock(:directory_reader)
13
+ @directory_reader.stub!(:read_files).and_return({ "test.html" => "contents" })
14
+ @test_runner = TestRunner.new(@test_pusher, @writer, @directory_reader)
15
+ end
16
+
17
+ def new_in_progress_test_result
18
+ test_result = mock :in_progress_test_result
19
+ test_result.stub!(:status).and_return "in progress"
20
+ test_result.stub!(:summary).and_return "in progress summary"
21
+ test_result.stub!(:completed_since).and_return([])
22
+ test_result
23
+ end
24
+
25
+ def new_passed_test_result
26
+ test_result = mock :passed_test_result
27
+ test_result.stub!(:status).and_return "pass"
28
+ test_result.stub!(:summary).and_return "pass summary"
29
+ test_result.stub!(:completed_since).and_return([])
30
+ test_result
31
+ end
32
+
33
+ def new_failed_test_result
34
+ test_result = mock :failed_test_result
35
+ test_result.stub!(:status).and_return "fail"
36
+ test_result.stub!(:summary).and_return "fail summary"
37
+ test_result.stub!(:completed_since).and_return([])
38
+ test_result
39
+ end
40
+
41
+ def new_timeout_test_result
42
+ test_result = mock :timeout_test_result
43
+ test_result.stub!(:status).and_return "timed out"
44
+ test_result.stub!(:summary).and_return "timed out summary"
45
+ test_result.stub!(:completed_since).and_return([])
46
+ test_result
47
+ end
48
+
49
+ it "reads a test" do
50
+ @test_pusher.stub!(:push)
51
+ @directory_reader.should_receive(:read_files).and_return(:file_body)
52
+ @test_runner.run_tests("foo_test")
53
+ end
54
+
55
+ it "gets the test name from the base name of the pattern and pushes the test" do
56
+ @test_pusher.should_receive(:push).with({:name => 'foo_test', :contents => { "test.html" => "contents" }.to_json })
57
+ @test_runner.run_tests("path/to/foo_test")
58
+ end
59
+
60
+ context "timeout" do
61
+ it 'returns a exit status of 2' do
62
+ @test_pusher.stub!(:push).and_yield(new_timeout_test_result)
63
+ Kernel.should_receive(:exit).with(2)
64
+ @test_runner.run_tests("foo_test")
65
+ end
66
+ end
67
+
68
+ context "failing test" do
69
+ before do
70
+ @test_pusher.stub!(:push).and_yield(new_failed_test_result)
71
+ end
72
+
73
+ it "only writes once" do
74
+ @writer.should_receive(:write_failing).once
75
+ @test_runner.run_tests("foo_test")
76
+ end
77
+
78
+ it "writes out an error code" do
79
+ Kernel.should_receive(:exit).with(1)
80
+ @test_runner.run_tests("foo_test")
81
+ end
82
+ end
83
+
84
+ context "passing test" do
85
+ it "writes passing output" do
86
+ @test_pusher.stub!(:push).and_yield(new_passed_test_result)
87
+ @writer.should_receive(:write_passing).with("pass summary")
88
+ @test_runner.run_tests("foo_test")
89
+ end
90
+ end
91
+
92
+ context "in progress" do
93
+ it "writes nothing" do
94
+ @test_pusher.stub!(:push).and_yield(new_in_progress_test_result)
95
+ @writer = mock(:writer)
96
+ @writer.should_not_receive(:write_passing)
97
+ @writer.should_not_receive(:write_failing)
98
+ TestRunner.new(@test_pusher, @writer, @directory_reader).run_tests("foo_test")
99
+ end
100
+ end
101
+
102
+ context "in progress, then passed result yielded" do
103
+ it "only writes out the summary when the status is not 'in progress'" do
104
+ in_progress_test_result = new_in_progress_test_result
105
+ pass_test_result = new_passed_test_result
106
+ @test_pusher.stub!(:push).and_yield(in_progress_test_result).and_yield(pass_test_result)
107
+ @writer.should_receive(:write_passing).with("pass summary").once
108
+ @test_runner.run_tests("foo_test")
109
+ end
110
+ end
111
+
112
+ context "in progress twice then passed result yielded" do
113
+ it "writes the difference between test results on each iteration" do
114
+ in_progress_one = new_in_progress_test_result
115
+ in_progress_two = new_in_progress_test_result
116
+ @test_pusher.stub!(:push).and_yield(in_progress_one).and_yield(in_progress_two)
117
+
118
+ in_progress_two.stub!(:completed_since).with(in_progress_one).and_return([
119
+ mock(:status_one, :status => "pass", :user_agent => "ipad", :test_name => "foo"),
120
+ mock(:status_one, :status => "fail", :user_agent => "iphone", :test_name => "bar")
121
+ ])
122
+
123
+ @writer.should_receive(:write_passing).with("PASS foo on ipad").once
124
+ @writer.should_receive(:write_failing).with("FAIL bar on iphone").once
125
+
126
+ @test_runner.run_tests("foo bar")
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,31 @@
1
+ require 'spassky/client/writer'
2
+
3
+ module Spassky::Client
4
+ describe DefaultWriter do
5
+ it "writes failures in no colour" do
6
+ output = mock(:output)
7
+ output.should_receive(:puts).with("hello")
8
+ DefaultWriter.new(output).write_failing("hello")
9
+ end
10
+
11
+ it "writes passes in no colour" do
12
+ output = mock(:output)
13
+ output.should_receive(:puts).with("hello")
14
+ DefaultWriter.new(output).write_passing("hello")
15
+ end
16
+ end
17
+
18
+ describe ColouredWriter do
19
+ it "writes failures in red" do
20
+ output = mock(:output)
21
+ output.should_receive(:puts).with("\e[31mhello\e[0m")
22
+ ColouredWriter.new(output).write_failing("hello")
23
+ end
24
+
25
+ it "writes passes in green" do
26
+ output = mock(:output)
27
+ output.should_receive(:puts).with("\e[32mhello\e[0m")
28
+ ColouredWriter.new(output).write_passing("hello")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,177 @@
1
+ require 'spec_helper'
2
+
3
+ require 'capybara'
4
+ require 'capybara/dsl'
5
+ require 'rack/test'
6
+ require 'json'
7
+
8
+ ENV["RACK_ENV"] = "test"
9
+ require 'spassky/server/app'
10
+
11
+ module Spassky::Server
12
+ describe App do
13
+ include Rack::Test::Methods
14
+
15
+ before do
16
+ RandomStringGenerator.stub!(:random_string).and_return("random-string")
17
+ end
18
+
19
+ def app
20
+ App.new(device_list)
21
+ end
22
+
23
+ let :device_list do
24
+ mock(:device_list, :update_last_connected => true, :recently_connected_devices => [])
25
+ end
26
+
27
+ describe "GET /device/connect" do
28
+ it "redirects to a unique URL" do
29
+ RandomStringGenerator.should_receive(:random_string).and_return("random-string")
30
+ get "/device/connect"
31
+ last_response.should be_redirect
32
+ last_response.location.should == "http://example.org/device/idle/random-string"
33
+ end
34
+ end
35
+
36
+ describe "GET /device/idle/123" do
37
+
38
+ it "tells the device list that the device connected" do
39
+ device_list.should_receive(:update_last_connected).with("some user agent")
40
+ header "User-Agent", "some user agent"
41
+ get "/device/idle/123"
42
+ end
43
+
44
+ context "when there are no tests to run on the connected device" do
45
+ it "serves HTML page with a meta-refresh tag" do
46
+ TestRun.stub!(:find_next_to_run_for_user_agent).and_return(nil)
47
+ RandomStringGenerator.should_receive(:random_string).and_return("next-iteration")
48
+ get "/device/idle/123"
49
+ last_response.body.should include("<meta http-equiv=\"refresh\" content=\"1; url='/device/idle/next-iteration'\">")
50
+ end
51
+ end
52
+
53
+ context "when there is a test to run on the connected device" do
54
+ it "redirects to /test_runs/:id/run/:random/test_name" do
55
+ RandomStringGenerator.stub!(:random_string).and_return("a-random-string")
56
+ test = mock(:test, :contents => "test contents")
57
+ test.stub!(:id).and_return("the-test-id")
58
+ test.stub!(:name).and_return("the-test-name")
59
+ TestRun.stub!(:find_next_to_run_for_user_agent).with("some user agent").and_return(test)
60
+ header "User-Agent", "some user agent"
61
+ get '/device/idle/123'
62
+ last_response.should be_redirect
63
+ last_response.location.should == 'http://example.org/test_runs/the-test-id/run/a-random-string/the-test-name'
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "GET /test_runs/:id/run/:random/assert" do
69
+ it "saves the test result" do
70
+ test = mock(:test)
71
+ TestRun.stub!(:find).with('123').and_return(test)
72
+ header "User-Agent", "the user agent"
73
+ test.should_receive(:save_results_for_user_agent).with(:user_agent => "the user agent", :status => "pass")
74
+ get "/test_runs/123/run/random/assert?status=pass"
75
+ end
76
+ end
77
+
78
+ describe "GET /test_runs/:id/run/:random/filename" do
79
+ before do
80
+ @test_contents = {
81
+ "test_file.js" => "some javascript",
82
+ "fake_test.html.file" => "don't choose this one",
83
+ "test_name.html" => "actual test!"
84
+ }
85
+ end
86
+
87
+ context "file name is a file" do
88
+ it "returns the specified file" do
89
+ test = mock(:test, :name => "fake_test.html.file", :contents => @test_contents)
90
+ TestRun.stub!(:find).with('123').and_return(test)
91
+ header "User-Agent", "some user agent"
92
+ get "/test_runs/123/run/random/fake_test.html.file"
93
+ last_response.body.should include("don't choose this one")
94
+ end
95
+ end
96
+
97
+ context "file name is not a file" do
98
+ it "returns the first file that ends with .html" do
99
+ test = mock(:test, :name => "directory/test_name.html", :contents => @test_contents)
100
+ TestRun.stub!(:find).with('123').and_return(test)
101
+ header "User-Agent", "some user agent"
102
+ get "/test_runs/123/run/random/not_a_file"
103
+ last_response.body.should include("actual test!")
104
+ end
105
+ end
106
+
107
+ describe "when the test contents includes a </head> tag" do
108
+ before do
109
+ @test_contents["test_name.html"] = "</head>"
110
+ @test = mock(:test, :name => "test_name", :contents => @test_contents)
111
+ TestRun.stub!(:find).with('123').and_return(@test)
112
+ end
113
+
114
+ it "adds a meta-refresh tag to the test contents" do
115
+ RandomStringGenerator.stub!(:random_string).and_return("next-iteration")
116
+ get "/test_runs/123/run/random/test_name.html"
117
+ url = "/device/idle/next-iteration"
118
+ last_response.body.should include("<meta http-equiv=\"refresh\" content=\"1; url='#{url}'\"></head>")
119
+ end
120
+
121
+ it "adds the assert.js script to the head" do
122
+ File.stub!(:read).and_return("assert.js!")
123
+ get "/test_runs/123/run/random/test_name.html"
124
+ last_response.body.should include("<script type=\"text/javascript\">assert.js!</script>")
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "POST /test_runs" do
130
+ before do
131
+ device_list.stub!(:recently_connected_devices).and_return(["foo", "bar"])
132
+ @test_run = mock(:test_run)
133
+ @test_run.stub!(:id).and_return(42)
134
+ TestRun.stub!(:create).and_return(@test_run)
135
+ RandomStringGenerator.stub!(:random_string).and_return("number")
136
+ @test_contents = {"file1" => "file1 contents", "file2" => "file2 contents"}
137
+ end
138
+
139
+ it "creates a test run" do
140
+ TestRun.should_receive(:create).with(:name => "first-test", :contents => @test_contents, :devices => ["foo", "bar"])
141
+ post "/test_runs", { :name => "first-test", :contents => @test_contents.to_json }
142
+ end
143
+
144
+ it "redirects to the status page" do
145
+ post "/test_runs", { :name => "first-test", :contents => @test_contents.to_json}
146
+ last_response.should be_redirect
147
+ last_response.location.should =~ /test_runs\/42$/
148
+ end
149
+ end
150
+
151
+ describe "GET /test_runs/123" do
152
+ it "returns the status of the test with the id '123'" do
153
+ test_run = mock(:test_run)
154
+ test_result = mock(:test_result)
155
+ test_result.stub!(:to_json).and_return("the test run as json")
156
+ test_run.stub!(:result).and_return(test_result)
157
+ test_run.stub!(:update_connected_devices)
158
+ TestRun.should_receive(:find).with('123').and_return test_run
159
+ get '/test_runs/123'
160
+ last_response.body.should == "the test run as json"
161
+ end
162
+
163
+ it "tells the test run which devices are still connected" do
164
+ test_run = mock(:test_run)
165
+ test_result = mock(:test_result)
166
+ test_result.stub!(:to_json).and_return("the test run as json")
167
+ test_run.stub!(:result).and_return(test_result)
168
+ TestRun.stub!(:find).with('123').and_return test_run
169
+ still_connected_devices = ["device1", "device2"]
170
+ device_list.stub!(:recently_connected_devices).and_return(still_connected_devices)
171
+ test_run.should_receive(:update_connected_devices).with(still_connected_devices)
172
+ get '/test_runs/123'
173
+ end
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'spassky/server/device_list'
3
+
4
+ module Spassky::Server
5
+ describe DeviceList do
6
+ it "keeps track of when devices last connected" do
7
+ list = DeviceList.new
8
+ list.update_last_connected("foo")
9
+ list.update_last_connected("bar")
10
+ list.recently_connected_devices.should == ["foo", "bar"]
11
+ end
12
+
13
+ it "clears all devices" do
14
+ list = DeviceList.new
15
+ list.update_last_connected("foo")
16
+ list.update_last_connected("bar")
17
+ list.clear
18
+ list.recently_connected_devices.size.should == 0
19
+ end
20
+
21
+ it "ignores devices connected more than 3 seconds ago" do
22
+ list = DeviceList.new
23
+ now = Time.now
24
+ Time.stub!(:now).and_return(now - 3)
25
+ list.update_last_connected("a")
26
+ Time.stub!(:now).and_return(now - 2)
27
+ list.update_last_connected("b")
28
+ Time.stub!(:now).and_return(now)
29
+ list.recently_connected_devices.should == ["b"]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'spassky/server/random_string_generator'
3
+
4
+ module Spassky::Server
5
+ describe RandomStringGenerator do
6
+ it "returns the current ticks" do
7
+ now = mock
8
+ now.stub!(:to_i).and_return(123)
9
+ Time.stub!(:now).and_return(now)
10
+ RandomStringGenerator.random_string.should == "123"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'spassky/server/test_run'
3
+
4
+ module Spassky::Server
5
+ describe TestRun do
6
+ before do
7
+ TestRun.delete_all
8
+ end
9
+
10
+ it "can be created and retrieved" do
11
+ created = TestRun.create(:name => "a test", :contents => "the contents")
12
+ retrieved = TestRun.find(created.id)
13
+ created.should == retrieved
14
+ end
15
+
16
+ it "assigns each test run a unique id" do
17
+ test_run1 = TestRun.create(:name => "test run 1", :contents => "the contents")
18
+ test_run2 =TestRun.create(:name => "test run 2", :contents => "the contents")
19
+ test_run1.id.should_not == test_run2.id
20
+ end
21
+
22
+ it "finds a test run by it's id" do
23
+ first = TestRun.create(:name => "test run 1", :contents => "the contents")
24
+ second = TestRun.create(:name => "test run 2", :contents => "the contents")
25
+ TestRun.find(first.id.to_s).should == first
26
+ TestRun.find(second.id).should == second
27
+ end
28
+
29
+ it "returns the next test to run for each device" do
30
+ created = TestRun.create(:name => "test", :contents => "contents", :devices => ["x", "y"])
31
+ TestRun.find_next_to_run_for_user_agent("x").should == created
32
+ TestRun.find_next_to_run_for_user_agent("y").should == created
33
+ end
34
+
35
+ it "only returns a test run per user agent until results are saved" do
36
+ created = TestRun.create(:name => "another test", :contents => "the contents of the test", :devices => ["user agent 1"])
37
+ TestRun.find_next_to_run_for_user_agent("user agent 1").should == created
38
+ TestRun.find_next_to_run_for_user_agent("user agent 1").should == created
39
+ created.save_results_for_user_agent(:user_agent => "user agent 1", :status => "pass")
40
+ TestRun.find_next_to_run_for_user_agent("user agent 1").should be_nil
41
+ end
42
+
43
+ it "returns 'in progress' status per user agent until the results are saved" do
44
+ created = TestRun.create(:name => "another test", :contents => "the contents of the test")
45
+ created.result.status.should == 'in progress'
46
+ created.save_results_for_user_agent(:user_agent => "some user agent", :status => "pass")
47
+ created.result.status.should == 'pass'
48
+ end
49
+
50
+ it "returns failed if the saved tests results failed" do
51
+ created = TestRun.create(:name => "another test", :contents => "the contents of the test")
52
+ created.save_results_for_user_agent(:user_agent => "another user agent", :status => "fail")
53
+ created.result.status.should == 'fail'
54
+ end
55
+
56
+ it "rejects nonsense status" do
57
+ run = TestRun.create(:name => "another test", :contents => "the contents of the test")
58
+ lambda {
59
+ run.save_results_for_user_agent(:user_agent => "another user agent", :status => "wtf?")
60
+ }.should raise_error("wtf? is not a valid status")
61
+ end
62
+
63
+ it "creates an in progress test result from the device list" do
64
+ run = TestRun.create(
65
+ :name => "another test",
66
+ :contents => "the contents of the test",
67
+ :devices => ["device1", "device2"]
68
+ )
69
+ run.result.device_statuses.size.should == 2
70
+ run.result.device_statuses.first.status.should == "in progress"
71
+ run.result.device_statuses.last.status.should == "in progress"
72
+ end
73
+
74
+ it "updates the status of disconnected devices to 'timed out'" do
75
+ run = TestRun.create(
76
+ :name => "another test",
77
+ :contents => "the contents of the test",
78
+ :devices => ["device1", "device2"]
79
+ )
80
+ run.update_connected_devices(["device1"])
81
+ run.result.device_statuses.first.status.should == "in progress"
82
+ run.result.device_statuses.last.status.should == "timed out"
83
+ end
84
+
85
+ it "does not update the status of disconnected devices that have already passed or failed" do
86
+ run = TestRun.create(
87
+ :name => "another test",
88
+ :contents => "the contents of the test",
89
+ :devices => ["device1", "device2"]
90
+ )
91
+ run.save_results_for_user_agent(:user_agent => "device1", :status => "pass")
92
+ run.save_results_for_user_agent(:user_agent => "device2", :status => "fail")
93
+ run.update_connected_devices([])
94
+ run.result.device_statuses.first.status.should == "pass"
95
+ run.result.device_statuses.last.status.should == "fail"
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'spassky/test_result'
3
+
4
+ module Spassky
5
+ describe TestResult do
6
+ context "when no devices are connected" do
7
+ it "is in progress" do
8
+ TestResult.new([]).status.should == "in progress"
9
+ end
10
+ end
11
+
12
+ context "when one device passes" do
13
+ it "outputs a summary" do
14
+ test_result = TestResult.new([
15
+ Spassky::DeviceTestStatus.new('agent1', 'pass', 'test')
16
+ ])
17
+ test_result.summary.should == "1 test passed on 1 device"
18
+ end
19
+ end
20
+
21
+ context "when all devices pass" do
22
+ it "is a pass" do
23
+ TestResult.new([
24
+ Spassky::DeviceTestStatus.new('agent1', 'pass', 'test'),
25
+ Spassky::DeviceTestStatus.new('agent2', 'pass', 'test')
26
+ ]).status.should == "pass"
27
+ end
28
+
29
+ it "outputs a pluralised summary" do
30
+ test_result = TestResult.new([
31
+ Spassky::DeviceTestStatus.new('agent1', 'pass', 'test'),
32
+ Spassky::DeviceTestStatus.new('agent2', 'pass', 'test')
33
+ ])
34
+ test_result.summary.should == "1 test passed on 2 devices"
35
+ end
36
+ end
37
+
38
+ context "when any device fails" do
39
+ it "is a fail" do
40
+ TestResult.new([
41
+ Spassky::DeviceTestStatus.new('agent1', 'pass', 'test'),
42
+ Spassky::DeviceTestStatus.new('agent2', 'fail', 'test')
43
+ ]).status.should == "fail"
44
+ end
45
+
46
+ it "outputs a summary" do
47
+ test_result = TestResult.new([
48
+ Spassky::DeviceTestStatus.new('agent1', 'fail', 'test')
49
+ ])
50
+ test_result.summary.should == "1 test failed on 1 device"
51
+ end
52
+ end
53
+
54
+ context "when any test is still in progress" do
55
+ it "is a fail" do
56
+ TestResult.new([
57
+ Spassky::DeviceTestStatus.new('agent1', 'pass', 'test'),
58
+ Spassky::DeviceTestStatus.new('agent2', 'fail', 'test'),
59
+ Spassky::DeviceTestStatus.new('agent3', 'in progress', 'test')
60
+ ]).status.should == "in progress"
61
+ end
62
+ end
63
+
64
+ context "when 1 test times out" do
65
+ it "outputs the correct summary" do
66
+ test_result = TestResult.new([
67
+ Spassky::DeviceTestStatus.new('agent1', 'timed out', 'test')
68
+ ])
69
+ test_result.summary.should == "1 test timed out on 1 device"
70
+ end
71
+
72
+ it "has the status 'timed out'" do
73
+ test_result = TestResult.new([
74
+ Spassky::DeviceTestStatus.new('agent1', 'timed out', 'test'),
75
+ Spassky::DeviceTestStatus.new('agent2', 'pass', 'test')
76
+ ])
77
+ test_result.status.should == "timed out"
78
+ end
79
+ end
80
+
81
+ it "can be serialized and deserialized" do
82
+ test_result = TestResult.new([Spassky::DeviceTestStatus.new('agent', 'pass', 'test')])
83
+ json = test_result.to_json
84
+ deserialized = TestResult.from_json(json)
85
+ deserialized.device_statuses.size.should == 1
86
+ deserialized.device_statuses.first.user_agent.should == 'agent'
87
+ deserialized.device_statuses.first.status.should == 'pass'
88
+ end
89
+
90
+ describe "#completed_since(nil)" do
91
+ it "returns all device test results that are not in progress" do
92
+ status_1 = Spassky::DeviceTestStatus.new('agent', 'pass', 'test1')
93
+ status_2 = Spassky::DeviceTestStatus.new('agent', 'in progress', 'test2')
94
+ status_3 = Spassky::DeviceTestStatus.new('agent', 'fail', 'test3')
95
+ test_result = TestResult.new([status_1, status_2, status_3])
96
+ test_result.completed_since(nil).should == [status_1, status_3]
97
+ end
98
+ end
99
+
100
+ describe "#completed_since(other_test_result)" do
101
+ it "returns all device test results that are no longer in progress" do
102
+ status_a1 = Spassky::DeviceTestStatus.new('agent', 'pass', 'test1')
103
+ status_a2 = Spassky::DeviceTestStatus.new('agent', 'in progress', 'test2')
104
+ status_a3 = Spassky::DeviceTestStatus.new('agent', 'in progress', 'test3')
105
+ status_b1 = Spassky::DeviceTestStatus.new('agent', 'pass', 'test1')
106
+ status_b2 = Spassky::DeviceTestStatus.new('agent', 'fail', 'test2')
107
+ status_b3 = Spassky::DeviceTestStatus.new('agent', 'timed out', 'test3')
108
+
109
+ test_result_before = TestResult.new([status_a1, status_a2, status_a3])
110
+ test_result_after = TestResult.new([status_b1, status_b2, status_b3])
111
+
112
+ test_result_after.completed_since(test_result_before).should == [status_b2, status_b3]
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ $:.unshift(File.dirname(__FILE__))
3
+ require 'spassky'