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,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'