testdroid-api 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.md +19 -0
- data/README.md +130 -0
- data/Rakefile +1 -0
- data/lib/testdroid-api.rb +8 -0
- data/lib/testdroid-api/client.rb +127 -0
- data/lib/testdroid-api/client/device_group.rb +18 -0
- data/lib/testdroid-api/client/project.rb +107 -0
- data/lib/testdroid-api/client/project/test_run.rb +79 -0
- data/lib/testdroid-api/client/project/test_run/device_run.rb +94 -0
- data/lib/testdroid-api/version.rb +3 -0
- data/spec/client_spec.rb +134 -0
- data/spec/device_run_spec.rb +108 -0
- data/spec/fixtures/apiUsers.json +96 -0
- data/spec/fixtures/app.apk +1 -0
- data/spec/fixtures/device_run.json +11 -0
- data/spec/fixtures/device_runs.json +35 -0
- data/spec/fixtures/devices.json +34 -0
- data/spec/fixtures/finished_run.json +5 -0
- data/spec/fixtures/junit.xml +24 -0
- data/spec/fixtures/junit.zip +0 -0
- data/spec/fixtures/logs.txt +1 -0
- data/spec/fixtures/logs.zip +0 -0
- data/spec/fixtures/new_project.json +7 -0
- data/spec/fixtures/new_run.json +5 -0
- data/spec/fixtures/projects.json +22 -0
- data/spec/fixtures/test_runs.json +13 -0
- data/spec/fixtures/waiting_device_run.json +11 -0
- data/spec/project_spec.rb +167 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/test_run_spec.rb +159 -0
- data/testdroidapi.gemspec +29 -0
- metadata +214 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
module TestdroidApi
|
2
|
+
class Client
|
3
|
+
class Project
|
4
|
+
class TestRun
|
5
|
+
|
6
|
+
attr_reader :project_id, :id, :name, :state
|
7
|
+
def initialize(client, project, config)
|
8
|
+
@client = client
|
9
|
+
@project_id = project.id
|
10
|
+
|
11
|
+
update(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Updates test_run information
|
15
|
+
def update!
|
16
|
+
res_name = "run"
|
17
|
+
endpoint = "projects/#{@project_id}/runs/#{id}"
|
18
|
+
|
19
|
+
config = @client.get_api_request(endpoint, res_name)
|
20
|
+
|
21
|
+
update(config)
|
22
|
+
end
|
23
|
+
|
24
|
+
def results
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns devices that the run was executed on.
|
29
|
+
# @return [Array<TestdroidApi::Client::TestRun::DeviceRun>]
|
30
|
+
def device_runs
|
31
|
+
res_name = "deviceRuns"
|
32
|
+
endpoint = "projects/#{@project_id}/runs/#{id}/device-runs"
|
33
|
+
|
34
|
+
results = @client.get_api_request(endpoint, res_name)
|
35
|
+
results.map{|device_run|
|
36
|
+
TestdroidApi::Client::Project::TestRun::DeviceRun.new(@client, self, device_run)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get test run's screenshots as a zip file.
|
41
|
+
def screenshots_zip
|
42
|
+
res_name = 'screenshots.zip'
|
43
|
+
endpoint = "projects/#{@project_id}/runs/#{id}/screenshots.zip"
|
44
|
+
|
45
|
+
@client.get_file(endpoint, res_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Get test run's junit results as a zip file
|
49
|
+
def junit_results_zip
|
50
|
+
res_name = 'junits.zip'
|
51
|
+
endpoint = "projects/#{@project_id}/runs/#{id}/junits.zip"
|
52
|
+
|
53
|
+
@client.get_file(endpoint, res_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get test run's logs as a zip file.
|
57
|
+
def logs_zip
|
58
|
+
res_name = 'logs.zip'
|
59
|
+
endpoint = "projects/#{@project_id}/runs/#{id}/logs.zip"
|
60
|
+
|
61
|
+
@client.get_file(endpoint, res_name)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Did test run finish
|
65
|
+
# @return [Boolean]
|
66
|
+
def finished?
|
67
|
+
state == 'FINISHED'
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def update(config)
|
72
|
+
@id = config['id']
|
73
|
+
@name = config['displayName']
|
74
|
+
@state = config['groupState']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module TestdroidApi
|
2
|
+
class Client
|
3
|
+
class Project
|
4
|
+
class TestRun
|
5
|
+
class DeviceRun
|
6
|
+
|
7
|
+
attr_reader :id, :device_id, :fail_message, :name, :state, :finished
|
8
|
+
attr_reader :screenshots_url, :junit_url, :logs_url
|
9
|
+
|
10
|
+
def initialize(client, run, config)
|
11
|
+
@client = client
|
12
|
+
@project_id = run.project_id
|
13
|
+
@run_id = run.id
|
14
|
+
|
15
|
+
update(config)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Updates device run information
|
19
|
+
def update!
|
20
|
+
res_name = 'deviceRun'
|
21
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}"
|
22
|
+
|
23
|
+
config = @client.get_api_request(endpoint, res_name)
|
24
|
+
|
25
|
+
update(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get device run's test results as JUnit XML
|
29
|
+
def junit_results
|
30
|
+
res_name = 'junit XML'
|
31
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}_junit.xml"
|
32
|
+
|
33
|
+
@client.get_file(endpoint, res_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
#Get device run's logs
|
37
|
+
def logs
|
38
|
+
res_name = 'log'
|
39
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}_log.txt"
|
40
|
+
|
41
|
+
@client.get_file(endpoint, res_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get device run's screenshots as a zip file.
|
45
|
+
def screenshots_zip
|
46
|
+
res_name = 'screenshots.zip'
|
47
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}/screenshots.zip"
|
48
|
+
|
49
|
+
@client.get_file(endpoint, res_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns screenshots' ids for device run.
|
53
|
+
def screenshots
|
54
|
+
res_name = 'deviceRunScreenshots'
|
55
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}/screenshots"
|
56
|
+
|
57
|
+
@client.get_api_request(endpoint, res_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Get device's run screenshot.
|
61
|
+
# @param screenshot_id [Integer] screenshot id
|
62
|
+
def screenshot(screenshot_id)
|
63
|
+
res_name = 'screenshot'
|
64
|
+
endpoint = "projects/#{@project_id}/runs/#{@run_id}/device-runs/#{id}/screenshots/#{screenshot_id}"
|
65
|
+
|
66
|
+
@client.get_file(endpoint, res_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Did test run finish
|
70
|
+
def finished?
|
71
|
+
finished
|
72
|
+
end
|
73
|
+
|
74
|
+
def results
|
75
|
+
raise NotImplementedError
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def update(config)
|
80
|
+
@id = config['id']
|
81
|
+
@device_id = config['deviceId']
|
82
|
+
@fail_message = config['customerFailureMessage']
|
83
|
+
@name = config['deviceName']
|
84
|
+
@state = config['groupState']
|
85
|
+
@finished = config['finished']
|
86
|
+
@screenshots_url = config['screenshotsURI']
|
87
|
+
@junit_url = config['junitURI']
|
88
|
+
@logs_url = config['logURI']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TestdroidApi::Client do
|
4
|
+
let(:client_class) { described_class }
|
5
|
+
let(:client) {client_class.new("user", "password")}
|
6
|
+
|
7
|
+
let(:auth_url) {"http://users.testdroid.com/api/v1/authorize"}
|
8
|
+
before do
|
9
|
+
stub_request(:post, auth_url).
|
10
|
+
to_return(:status => 200, :body => {"secretApiKey" => "1"}.to_json, :headers => {})
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#authenticate' do
|
14
|
+
it 'sends the right request' do
|
15
|
+
client.authenticate!
|
16
|
+
|
17
|
+
a_request(:post, auth_url).
|
18
|
+
with(:body => {:email => "user", :password => "password" }).
|
19
|
+
should have_been_made
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when successful' do
|
23
|
+
it 'stores apiKey' do
|
24
|
+
client.authenticate!
|
25
|
+
|
26
|
+
client.instance_variable_get('@api_key').should == '1'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when fails' do
|
31
|
+
it 'throws exception' do
|
32
|
+
stub_request(:post, "http://users.testdroid.com/api/v1/authorize").
|
33
|
+
to_return(:status => 200, :body => {}.to_json, :headers => {})
|
34
|
+
|
35
|
+
expect { client.authenticate! }.
|
36
|
+
to raise_error(RuntimeError, "Could not authenticate, are you sure you have the right credentials?")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when authenticated' do
|
42
|
+
before do
|
43
|
+
client.authenticate!
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#projects' do
|
47
|
+
let(:url) {"https://cloud.testdroid.com/api/v1/projects"}
|
48
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/projects.json").read}
|
49
|
+
|
50
|
+
before do
|
51
|
+
stub_request(:get, url).
|
52
|
+
to_return(:body => JSON.parse(response_body).to_json)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sends the right request' do
|
56
|
+
client.projects
|
57
|
+
|
58
|
+
a_request(:get, url).should have_been_made
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'without any params' do
|
62
|
+
it 'should return array of projects' do
|
63
|
+
TestdroidApi::Client::Project.stub(:new).with(any_args).and_return(true)
|
64
|
+
|
65
|
+
client.projects.should == [true, true]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with project names' do
|
70
|
+
it 'finds the matching projects' do
|
71
|
+
client.projects('Project 1').first.name.should == "Project 1"
|
72
|
+
end
|
73
|
+
it 'removes unmatched projects' do
|
74
|
+
client.projects('Project 1').count.should == 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#create_project' do
|
80
|
+
let(:url) {"https://cloud.testdroid.com/api/v1/projects"}
|
81
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/new_project.json").read}
|
82
|
+
before do
|
83
|
+
stub_request(:post, url).
|
84
|
+
to_return(:status => 200, :body => JSON.parse(response_body).to_json, :headers => {})
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'sends the right request' do
|
88
|
+
client.create_project("Name", "Description" )
|
89
|
+
|
90
|
+
a_request(:post, url).
|
91
|
+
with(:body => {:name => "Name", :description => "Description"} ).
|
92
|
+
should have_been_made
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns new project' do
|
96
|
+
client.create_project(nil, nil).name.should == "New project name"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#device groups' do
|
101
|
+
let(:url) {url = "https://cloud.testdroid.com/api/v1/clusters"}
|
102
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/devices.json").read}
|
103
|
+
before do
|
104
|
+
stub_request(:get, url).
|
105
|
+
to_return(:status => 200, :body => JSON.parse(response_body).to_json, :headers => {})
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'sends the right request' do
|
109
|
+
client.device_groups
|
110
|
+
|
111
|
+
a_request(:get, url).
|
112
|
+
should have_been_made
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'without params' do
|
116
|
+
it 'returns all device groups' do
|
117
|
+
client.device_groups("Test devices").first.display_name.should == "Test devices"
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'removes unmatched projects' do
|
121
|
+
client.device_groups("Test devices").count.should == 1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when not authenticated' do
|
129
|
+
it 'does not allow to fetch projects' do
|
130
|
+
expect{client.projects}.
|
131
|
+
to raise_error(RuntimeError, "Are you sure you've authenticated?")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe TestdroidApi::Client::Project::TestRun::DeviceRun do
|
4
|
+
|
5
|
+
let(:auth_url) { "http://users.testdroid.com/api/v1/authorize" }
|
6
|
+
let(:client) { TestdroidApi::Client.new('username', 'password') }
|
7
|
+
let(:run) { double(TestdroidApi::Client::Project::TestRun) }
|
8
|
+
let(:config) { JSON.parse(File.new(File.dirname(__FILE__) + "/fixtures/device_run.json").read)}
|
9
|
+
before do
|
10
|
+
stub_request(:post, auth_url).
|
11
|
+
to_return(:status => 200, :body => {"secretApiKey" => "1"}.to_json, :headers => {})
|
12
|
+
client.authenticate!
|
13
|
+
|
14
|
+
run.stub(:project_id).and_return(1)
|
15
|
+
run.stub(:id).and_return(100)
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when authenticated' do
|
19
|
+
let(:device_run) { described_class.new(client, run, config) }
|
20
|
+
|
21
|
+
describe '#update!' do
|
22
|
+
let(:url) { "https://cloud.testdroid.com/api/v1/projects/1/runs/100/device-runs/1000" }
|
23
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/waiting_device_run.json").read}
|
24
|
+
before do
|
25
|
+
stub_request(:get, url).
|
26
|
+
to_return(:status => 200, :body => JSON.parse(response_body).to_json, :headers => {})
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sends the right request' do
|
30
|
+
device_run.update!
|
31
|
+
|
32
|
+
a_request(:get, url).
|
33
|
+
should have_been_made
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'updates run state' do
|
37
|
+
device_run.update!
|
38
|
+
device_run.state.should == "WAITING"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
describe '#finished' do
|
44
|
+
let(:url) { "https://cloud.testdroid.com/api/v1/projects/1/runs/100/device-runs/1000" }
|
45
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/waiting_device_run.json").read}
|
46
|
+
before do
|
47
|
+
stub_request(:get, url).
|
48
|
+
to_return(:status => 200, :body => JSON.parse(response_body).to_json, :headers => {})
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns true when run is finished' do
|
52
|
+
device_run.should be_finished
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns false when run it not finished' do
|
56
|
+
device_run.update!
|
57
|
+
device_run.should_not be_finished
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#logs_zip' do
|
62
|
+
let(:url) { "https://cloud.testdroid.com/api/v1/projects/1/runs/100/device-runs/1000_log.txt" }
|
63
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/logs.txt")}
|
64
|
+
before do
|
65
|
+
stub_request(:get, url).
|
66
|
+
to_return(:status => 200, :body => response_body, :headers => {})
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'sends the right request' do
|
70
|
+
device_run.logs
|
71
|
+
|
72
|
+
a_request(:get, url).should have_been_made
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#screenshots_zip' do
|
78
|
+
let(:url) { "https://cloud.testdroid.com/api/v1/projects/1/runs/100/device-runs/1000/screenshots/1000" }
|
79
|
+
|
80
|
+
before do
|
81
|
+
stub_request(:get, url).
|
82
|
+
to_return(:status => 200, :body => "", :headers => {})
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'sends the right request' do
|
86
|
+
device_run.screenshot(1000)
|
87
|
+
|
88
|
+
a_request(:get, url).should have_been_made
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#junit_results' do
|
94
|
+
let(:url) { "https://cloud.testdroid.com/api/v1/projects/1/runs/100/device-runs/1000_junit.xml" }
|
95
|
+
let(:response_body) {File.new(File.dirname(__FILE__) + "/fixtures/junit.xml")}
|
96
|
+
before do
|
97
|
+
stub_request(:get, url).
|
98
|
+
to_return(:status => 200, :body => response_body, :headers => {})
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'sends the right request' do
|
102
|
+
device_run.junit_results
|
103
|
+
|
104
|
+
a_request(:get, url).should have_been_made
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
{
|
2
|
+
"address": "",
|
3
|
+
"name": "User Surname",
|
4
|
+
"id": 1333333,
|
5
|
+
"state": "",
|
6
|
+
"country": "DE",
|
7
|
+
"displayCountry": "Germany",
|
8
|
+
"timeZone": "GMT",
|
9
|
+
"username": "132254",
|
10
|
+
"displayname": "User user",
|
11
|
+
"enabled": true,
|
12
|
+
"active": true,
|
13
|
+
"code": "",
|
14
|
+
"email": "user@soundcloud.com",
|
15
|
+
"googleId": null,
|
16
|
+
"billing": false,
|
17
|
+
"organization": {
|
18
|
+
"name": "",
|
19
|
+
"id": 1
|
20
|
+
},
|
21
|
+
"countryDisplayname": "Germany",
|
22
|
+
"city": "",
|
23
|
+
"phone": "",
|
24
|
+
"secretApiKey": "xxxxxxxxxxxxxxxxx",
|
25
|
+
"Organization role": "Other",
|
26
|
+
"vatID": "",
|
27
|
+
"accessible": true,
|
28
|
+
"activeRoles": [
|
29
|
+
{
|
30
|
+
"name": "TEST_RUN_SCREENSHOTS",
|
31
|
+
"active": true,
|
32
|
+
"timeLimitUsed": false,
|
33
|
+
"useLimit": 100,
|
34
|
+
"useLimited": true
|
35
|
+
},
|
36
|
+
{
|
37
|
+
"name": "FREE_RUN",
|
38
|
+
"active": true,
|
39
|
+
"timeLimitUsed": false,
|
40
|
+
"useLimit": 115,
|
41
|
+
"useLimited": true
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"name": "PRIORITY_GOLD",
|
45
|
+
"active": true,
|
46
|
+
"timeLimitUsed": true,
|
47
|
+
"useLimit": 0,
|
48
|
+
"useLimited": false
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"name": "PAID_RUN",
|
52
|
+
"active": true,
|
53
|
+
"timeLimitUsed": true,
|
54
|
+
"useLimit": 0,
|
55
|
+
"useLimited": false
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"name": "API_CLIENT",
|
59
|
+
"active": true,
|
60
|
+
"timeLimitUsed": true,
|
61
|
+
"useLimit": 0,
|
62
|
+
"useLimited": false
|
63
|
+
}
|
64
|
+
],
|
65
|
+
"lastLoginTime": 1380651077429,
|
66
|
+
"registrationIP": null,
|
67
|
+
"purchasedUserServices": [
|
68
|
+
|
69
|
+
],
|
70
|
+
"creditsLeft": "10000",
|
71
|
+
"Heard from": "Other",
|
72
|
+
"availableHearFromValues": [
|
73
|
+
"COWORKER",
|
74
|
+
"EMAIL_CAMPAIGN",
|
75
|
+
"EVENT",
|
76
|
+
"ONLINE_COMMUNITY",
|
77
|
+
"SOCIAL_NETWORK",
|
78
|
+
"WEB_SEARCH",
|
79
|
+
"OTHER"
|
80
|
+
],
|
81
|
+
"availableOrganizationRoleValues": [
|
82
|
+
"CEO_OR_OWNER",
|
83
|
+
"VP",
|
84
|
+
"MANAGER",
|
85
|
+
"INDIVIDUAL",
|
86
|
+
"QUALITY_ASSURANCE",
|
87
|
+
"DEVELOPER",
|
88
|
+
"MARKETING",
|
89
|
+
"CONSULTANT",
|
90
|
+
"OTHER"
|
91
|
+
],
|
92
|
+
"admin": false,
|
93
|
+
"accountNonExpired": true,
|
94
|
+
"accountNonLocked": true,
|
95
|
+
"credentialsNonExpired": true
|
96
|
+
}
|