quilted-harvested 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +25 -0
- data/HISTORY +19 -0
- data/MIT-LICENSE +20 -0
- data/README.md +68 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/examples/basics.rb +35 -0
- data/examples/clear_account.rb +28 -0
- data/examples/task_assignments.rb +27 -0
- data/examples/user_assignments.rb +24 -0
- data/features/account.feature +7 -0
- data/features/client_contacts.feature +23 -0
- data/features/clients.feature +29 -0
- data/features/errors.feature +25 -0
- data/features/expense_categories.feature +21 -0
- data/features/expenses.feature +55 -0
- data/features/hardy_client.feature +40 -0
- data/features/projects.feature +39 -0
- data/features/reporting.feature +72 -0
- data/features/step_definitions/account_steps.rb +7 -0
- data/features/step_definitions/assignment_steps.rb +100 -0
- data/features/step_definitions/contact_steps.rb +11 -0
- data/features/step_definitions/debug_steps.rb +3 -0
- data/features/step_definitions/error_steps.rb +113 -0
- data/features/step_definitions/expenses_steps.rb +46 -0
- data/features/step_definitions/harvest_steps.rb +8 -0
- data/features/step_definitions/model_steps.rb +90 -0
- data/features/step_definitions/people_steps.rb +4 -0
- data/features/step_definitions/report_steps.rb +91 -0
- data/features/step_definitions/time_entry_steps.rb +40 -0
- data/features/support/env.rb +37 -0
- data/features/support/error_helpers.rb +18 -0
- data/features/support/fixtures/empty_clients.xml +2 -0
- data/features/support/fixtures/over_limit.xml +8 -0
- data/features/support/fixtures/receipt.png +0 -0
- data/features/support/fixtures/under_limit.xml +8 -0
- data/features/support/harvest_credentials.example.yml +4 -0
- data/features/support/harvest_helpers.rb +11 -0
- data/features/support/inflections.rb +9 -0
- data/features/task_assignment.feature +69 -0
- data/features/tasks.feature +25 -0
- data/features/time_tracking.feature +29 -0
- data/features/user_assignments.feature +33 -0
- data/features/users.feature +55 -0
- data/harvested.gemspec +159 -0
- data/lib/harvest/api/account.rb +15 -0
- data/lib/harvest/api/base.rb +42 -0
- data/lib/harvest/api/clients.rb +10 -0
- data/lib/harvest/api/contacts.rb +19 -0
- data/lib/harvest/api/expense_categories.rb +9 -0
- data/lib/harvest/api/expenses.rb +28 -0
- data/lib/harvest/api/projects.rb +54 -0
- data/lib/harvest/api/reports.rb +39 -0
- data/lib/harvest/api/task_assignments.rb +32 -0
- data/lib/harvest/api/tasks.rb +9 -0
- data/lib/harvest/api/time.rb +32 -0
- data/lib/harvest/api/user_assignments.rb +32 -0
- data/lib/harvest/api/users.rb +21 -0
- data/lib/harvest/base.rb +258 -0
- data/lib/harvest/base_model.rb +73 -0
- data/lib/harvest/behavior/activatable.rb +31 -0
- data/lib/harvest/behavior/crud.rb +57 -0
- data/lib/harvest/client.rb +30 -0
- data/lib/harvest/contact.rb +29 -0
- data/lib/harvest/credentials.rb +21 -0
- data/lib/harvest/errors.rb +23 -0
- data/lib/harvest/expense.rb +19 -0
- data/lib/harvest/expense_category.rb +18 -0
- data/lib/harvest/hardy_client.rb +80 -0
- data/lib/harvest/project.rb +56 -0
- data/lib/harvest/rate_limit_status.rb +28 -0
- data/lib/harvest/task.rb +30 -0
- data/lib/harvest/task_assignment.rb +34 -0
- data/lib/harvest/time_entry.rb +42 -0
- data/lib/harvest/timezones.rb +149 -0
- data/lib/harvest/user.rb +66 -0
- data/lib/harvest/user_assignment.rb +34 -0
- data/lib/harvested.rb +62 -0
- data/spec/harvest/base_spec.rb +9 -0
- data/spec/harvest/credentials_spec.rb +22 -0
- data/spec/harvest/expense_spec.rb +15 -0
- data/spec/harvest/task_assignment_spec.rb +10 -0
- data/spec/harvest/time_entry_spec.rb +22 -0
- data/spec/harvest/user_assignment_spec.rb +10 -0
- data/spec/harvest/user_spec.rb +32 -0
- data/spec/spec.default.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +264 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
When 'I create and assign a task "$1" to the project "$2"' do |task_name, project_name|
|
2
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
3
|
+
p = harvest_api.projects.create_task(project, task_name)
|
4
|
+
p.should == project
|
5
|
+
end
|
6
|
+
|
7
|
+
When 'I assign the task "$1" to the project "$2"' do |task_name, project_name|
|
8
|
+
task = Then %Q{there should be a task "#{task_name}"}
|
9
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
10
|
+
assignment = Harvest::TaskAssignment.new(:project => project, :task => task)
|
11
|
+
harvest_api.task_assignments.create(assignment)
|
12
|
+
end
|
13
|
+
|
14
|
+
Then 'the task "$1" should be assigned to the project "$2"' do |task_name, project_name|
|
15
|
+
task = Then %Q{there should be a task "#{task_name}"}
|
16
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
17
|
+
assignments = harvest_api.task_assignments.all(project)
|
18
|
+
assignment = assignments.detect {|a| a.project_id == project.to_i && a.task_id == task.to_i }
|
19
|
+
assignment.should_not be_nil
|
20
|
+
assignment
|
21
|
+
end
|
22
|
+
|
23
|
+
Then 'the task "$1" should not be assigned to the project "$2"' do |task_name, project_name|
|
24
|
+
task = Then %Q{there should be a task "#{task_name}"}
|
25
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
26
|
+
assignments = harvest_api.task_assignments.all(project)
|
27
|
+
assignment = assignments.detect {|a| a.project_id == project.to_i && a.task_id == task.to_i }
|
28
|
+
assignment.should be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
When 'I update the task "$1" for the project "$2" with the following:' do |task_name, project_name, table|
|
32
|
+
assignment = Then %Q{the task "#{task_name}" should be assigned to the project "#{project_name}"}
|
33
|
+
assignment.attributes = table.rows_hash
|
34
|
+
harvest_api.task_assignments.update(assignment)
|
35
|
+
end
|
36
|
+
|
37
|
+
Then 'the task "$1" for the project "$2" should have the following attributes:' do |task_name, project_name, table|
|
38
|
+
assignment = Then %Q{the task "#{task_name}" should be assigned to the project "#{project_name}"}
|
39
|
+
table.rows_hash.each do |key, value|
|
40
|
+
assignment.send(key).to_s.should == value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
When 'I remove the task "$1" from the project "$2"' do |task_name, project_name|
|
45
|
+
assignment = Then %Q{the task "#{task_name}" should be assigned to the project "#{project_name}"}
|
46
|
+
id = harvest_api.task_assignments.delete(assignment)
|
47
|
+
id.should == assignment.id
|
48
|
+
end
|
49
|
+
|
50
|
+
When 'I try to remove the task "$1" from the project "$2"' do |task_name, project_name|
|
51
|
+
assignment = Then %Q{the task "#{task_name}" should be assigned to the project "#{project_name}"}
|
52
|
+
begin
|
53
|
+
harvest_api.task_assignments.delete(assignment)
|
54
|
+
rescue Harvest::HTTPError => e
|
55
|
+
@error = e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
When 'I assign the user "$1" to the project "$2"' do |email, project_name|
|
60
|
+
user = Then %Q{there should be a user "#{email}"}
|
61
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
62
|
+
assignment = Harvest::UserAssignment.new(:project => project, :user => user)
|
63
|
+
harvest_api.user_assignments.create(assignment)
|
64
|
+
end
|
65
|
+
|
66
|
+
When 'I remove the user "$1" from the project "$2"' do |email, project_name|
|
67
|
+
assignment = Then %Q{the user "#{email}" should be assigned to the project "#{project_name}"}
|
68
|
+
id = harvest_api.user_assignments.delete(assignment)
|
69
|
+
id.should == assignment.id
|
70
|
+
end
|
71
|
+
|
72
|
+
Then 'the user "$1" should be assigned to the project "$2"' do |email, project_name|
|
73
|
+
user = Then %Q{there should be a user "#{email}"}
|
74
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
75
|
+
assignments = harvest_api.user_assignments.all(project)
|
76
|
+
assignment = assignments.detect {|a| a.project_id == project.to_i && a.user_id == user.to_i }
|
77
|
+
assignment.should_not be_nil
|
78
|
+
assignment
|
79
|
+
end
|
80
|
+
|
81
|
+
Then 'the user "$1" should not be assigned to the project "$2"' do |email, project_name|
|
82
|
+
user = Then %Q{there should be a user "#{email}"}
|
83
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
84
|
+
assignments = harvest_api.user_assignments.all(project)
|
85
|
+
assignment = assignments.detect {|a| a.project_id == project.to_i && a.user_id == user.to_i }
|
86
|
+
assignment.should be_nil
|
87
|
+
end
|
88
|
+
|
89
|
+
When 'I update the user "$1" on the project "$2" with the following:' do |email, project_name, table|
|
90
|
+
assignment = Then %Q{the user "#{email}" should be assigned to the project "#{project_name}"}
|
91
|
+
assignment.attributes = table.rows_hash
|
92
|
+
harvest_api.user_assignments.update(assignment)
|
93
|
+
end
|
94
|
+
|
95
|
+
Then 'the user "$1" on the project "$2" should have the following attributes:' do |email, project_name, table|
|
96
|
+
assignment = Then %Q{the user "#{email}" should be assigned to the project "#{project_name}"}
|
97
|
+
table.rows_hash.each do |key, value|
|
98
|
+
assignment.send(key).to_s.should == value
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Then 'there should be a contact "$1" for the client "$2"' do |email, client_name|
|
2
|
+
client = Then %Q{there should be a client "#{client_name}"}
|
3
|
+
contact = Then %Q{there should be a contact "#{email}"}
|
4
|
+
contact.client_id.should == client.id
|
5
|
+
|
6
|
+
contacts = harvest_api.contacts.all(client.id)
|
7
|
+
contact2 = contacts.detect {|c| c.email == email}
|
8
|
+
contact2.should == contact
|
9
|
+
|
10
|
+
contact
|
11
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
Given /^the next request will receive a (bad request|not found|bad gateway|server error|rate limit) response$/ do |type|
|
2
|
+
statuses = {
|
3
|
+
'bad request' => ['400', 'Bad Request'],
|
4
|
+
'not found' => ['404', 'Not Found'],
|
5
|
+
'bad gateway' => ['502', 'Bad Gateway'],
|
6
|
+
'server error' => ['500', 'Server Error'],
|
7
|
+
'rate limit' => ['503', 'Rake Limited']
|
8
|
+
}
|
9
|
+
|
10
|
+
if status = statuses[type]
|
11
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
12
|
+
{:status => status, :times => 1},
|
13
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
14
|
+
])
|
15
|
+
else
|
16
|
+
pending
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Given /^the next request will receive a rate limit response with a refresh in (\d+) seconds$/ do |seconds|
|
21
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
22
|
+
{:status => ['503', 'Rake Limited'], "Retry-After" => seconds},
|
23
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
24
|
+
])
|
25
|
+
end
|
26
|
+
|
27
|
+
Given 'the next request will receive a rate limit response without a refresh set' do
|
28
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
29
|
+
{:status => ['503', 'Rake Limited']},
|
30
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
31
|
+
])
|
32
|
+
end
|
33
|
+
|
34
|
+
When 'I make a request with the standard client' do
|
35
|
+
set_time_and_return_and_error do
|
36
|
+
standard_api.clients.all
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
When 'I make a request with the hardy client' do
|
41
|
+
set_time_and_return_and_error do
|
42
|
+
harvest_api.clients.all
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
When /^I make a request with the hardy client with (\d+) max retries$/ do |times|
|
47
|
+
set_time_and_return_and_error do
|
48
|
+
api = Harvest.hardy_client(@subdomain, @username, @password, :ssl => @ssl, :retry => times.to_i)
|
49
|
+
api.clients.all
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /a (\d+) error should be raised/ do |code|
|
54
|
+
case code
|
55
|
+
when '400'
|
56
|
+
@error.should be_a(Harvest::BadRequest)
|
57
|
+
when '404'
|
58
|
+
@error.should be_a(Harvest::NotFound)
|
59
|
+
when '502'
|
60
|
+
@error.should be_a(Harvest::Unavailable)
|
61
|
+
when '500'
|
62
|
+
@error.should be_a(Harvest::ServerError)
|
63
|
+
when '503'
|
64
|
+
@error.should be_a(Harvest::RateLimited)
|
65
|
+
else
|
66
|
+
pending
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Then /the hardy client should wait (\d+) seconds for the rate limit to reset/ do |seconds|
|
71
|
+
Time.now.should be_close(@time + seconds.to_i, 2)
|
72
|
+
end
|
73
|
+
|
74
|
+
Then 'I should be able to make a request again' do
|
75
|
+
harvest_api.clients.all
|
76
|
+
harvest_api.clients.all
|
77
|
+
end
|
78
|
+
|
79
|
+
Given /^the next (\d+) requests will receive a bad gateway response$/ do |times|
|
80
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
81
|
+
{:status => ['502', 'Bad Gateway'], :times => times.to_i},
|
82
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
83
|
+
])
|
84
|
+
end
|
85
|
+
|
86
|
+
Given /^the next (\d+) requests will receive a server error response$/ do |times|
|
87
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
88
|
+
{:status => ['500', 'Server Error'], :times => times.to_i},
|
89
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
90
|
+
])
|
91
|
+
end
|
92
|
+
|
93
|
+
Given /^the next (\d+) requests will receive an HTTP Error$/ do |times|
|
94
|
+
FakeWeb.register_uri(:get, /\/clients/, [
|
95
|
+
{:exception => Net::HTTPError, :times => times.to_i},
|
96
|
+
{:body => File.read(File.dirname(__FILE__) + '/../support/fixtures/empty_clients.xml')}
|
97
|
+
])
|
98
|
+
end
|
99
|
+
|
100
|
+
Then 'no errors should be raised' do
|
101
|
+
@error.should be_nil
|
102
|
+
@clients.should == []
|
103
|
+
end
|
104
|
+
|
105
|
+
Given 'the rate limit status indicates I\'m over my limit' do
|
106
|
+
over_limit_response = File.read(File.dirname(__FILE__) + '/../support/fixtures/over_limit.xml')
|
107
|
+
FakeWeb.register_uri(:get, /\/account\/rate_limit_status/, :body => over_limit_response)
|
108
|
+
end
|
109
|
+
|
110
|
+
Given 'the rate limit status indicates I\'m under my limit' do
|
111
|
+
over_limit_response = File.read(File.dirname(__FILE__) + '/../support/fixtures/under_limit.xml')
|
112
|
+
FakeWeb.register_uri(:get, /\/account\/rate_limit_status/, :body => over_limit_response)
|
113
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
When 'I create an expense for the project "$1" with the category "$2" with the following:' do |project_name, category_name, table|
|
2
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
3
|
+
category = Then %Q{there should be an expense category "#{category_name}"}
|
4
|
+
|
5
|
+
expense = Harvest::Expense.new(table.rows_hash.merge("expense_category_id" => category.id, "project_id" => project.id))
|
6
|
+
harvest_api.expenses.create(expense)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then 'there should be an expense "$1" on "$2"' do |notes, date|
|
10
|
+
expenses = harvest_api.expenses.all(date)
|
11
|
+
expense = expenses.detect {|e| e.notes == notes}
|
12
|
+
expense.should_not be_nil
|
13
|
+
expense
|
14
|
+
end
|
15
|
+
|
16
|
+
Then 'there should not be an expense "$1" on "$2"' do |notes, date|
|
17
|
+
expenses = harvest_api.expenses.all(date)
|
18
|
+
expense = expenses.detect {|e| e.notes == notes}
|
19
|
+
expense.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
When 'I delete the expense "$1" on "$2"' do |notes, date|
|
23
|
+
expense = Then %Q{there should be an expense "#{notes}" on "#{date}"}
|
24
|
+
id = harvest_api.expenses.delete(expense)
|
25
|
+
expense.id.should == id
|
26
|
+
end
|
27
|
+
|
28
|
+
Then 'the expense "$1" on "$2" should have the following attributes:' do |notes, date, table|
|
29
|
+
expense = Then %Q{there should be an expense "#{notes}" on "#{date}"}
|
30
|
+
table.rows_hash.each do |key, value|
|
31
|
+
expense.send(key).to_s.should == value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
When 'I update the expense "$1" on "$2" with the following:' do |notes, date, table|
|
36
|
+
expense = Then %Q{there should be an expense "#{notes}" on "#{date}"}
|
37
|
+
expense.attributes = table.rows_hash
|
38
|
+
harvest_api.expenses.update(expense)
|
39
|
+
end
|
40
|
+
|
41
|
+
When 'I attach the receipt "$1" to the expense "$2" on "$3"' do |path, notes, date|
|
42
|
+
path = "#{File.dirname(__FILE__)}/../#{path}"
|
43
|
+
receipt = StringIO.new(File.read(path))
|
44
|
+
expense = Then %Q{there should be an expense "#{notes}" on "#{date}"}
|
45
|
+
harvest_api.expenses.attach(expense, "receipt.png", receipt)
|
46
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Given 'I am using the credentials from "$1"' do |path|
|
2
|
+
raise "You need a credentials file in support/harvest_credentials.yml!!" unless File.exist?("#{File.dirname(__FILE__)}/../#{path}")
|
3
|
+
credentials = YAML.load_file("#{File.dirname(__FILE__)}/../#{path}")
|
4
|
+
@username = credentials["username"]
|
5
|
+
@password = credentials["password"]
|
6
|
+
@subdomain = credentials["subdomain"]
|
7
|
+
@ssl = credentials["ssl"]
|
8
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
When /^I create an? (client|task|user|expense category) with the following:$/ do |type, table|
|
2
|
+
case type
|
3
|
+
when "client"
|
4
|
+
client = Harvest::Client.new(table.rows_hash)
|
5
|
+
harvest_api.clients.create(client)
|
6
|
+
when "task"
|
7
|
+
task = Harvest::Task.new(table.rows_hash)
|
8
|
+
harvest_api.tasks.create(task)
|
9
|
+
when "user"
|
10
|
+
user = Harvest::User.new(table.rows_hash)
|
11
|
+
harvest_api.users.create(user)
|
12
|
+
when "expense category"
|
13
|
+
expense_cat = Harvest::ExpenseCategory.new(table.rows_hash)
|
14
|
+
harvest_api.expense_categories.create(expense_cat)
|
15
|
+
else pending end
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I create a (contact|project) for the client "([^\"]*)" with the following:$/ do |type, client_name, table|
|
19
|
+
client = Then %Q{there should be a client "#{client_name}"}
|
20
|
+
attributes = table.rows_hash.merge("client_id" => client.id)
|
21
|
+
|
22
|
+
case type
|
23
|
+
when "contact"
|
24
|
+
contact = Harvest::Contact.new(attributes)
|
25
|
+
harvest_api.contacts.create(contact)
|
26
|
+
when "project"
|
27
|
+
project = Harvest::Project.new(attributes)
|
28
|
+
harvest_api.projects.create(project)
|
29
|
+
else pending end
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^there should be an? (contact|project|client|task|user|expense category) "([^\"]*)"$/ do |type, identifier|
|
33
|
+
collection = harvest_api.send(pluralize(type)).all
|
34
|
+
attribute = case type
|
35
|
+
when 'contact', 'user' then 'email'
|
36
|
+
when 'project', 'client', 'task', 'expense category' then 'name'
|
37
|
+
else pending end
|
38
|
+
item = collection.detect {|c| c.send(attribute) == identifier}
|
39
|
+
item.should_not be_nil
|
40
|
+
item
|
41
|
+
end
|
42
|
+
|
43
|
+
Then /^there should not be an? (contact|project|client|task|user|expense category) "([^\"]*)"$/ do |type, identifier|
|
44
|
+
collection = harvest_api.send(pluralize(type)).all
|
45
|
+
attribute = case type
|
46
|
+
when 'contact', 'user' then 'email'
|
47
|
+
when 'project', 'client', 'task', 'expense category' then 'name'
|
48
|
+
else pending end
|
49
|
+
item = collection.detect {|c| c.send(attribute) == identifier}
|
50
|
+
item.should be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
When /^I update the (contact|project|client|task|user|expense category) "([^\"]*)" with the following:$/ do |type, name, table|
|
54
|
+
item = Then %Q{there should be a #{type} "#{name}"}
|
55
|
+
item.attributes = table.rows_hash
|
56
|
+
harvest_api.send(pluralize(type)).update(item)
|
57
|
+
end
|
58
|
+
|
59
|
+
Then /^the (contact|project|client|task|user|expense category) "([^\"]*)" should have the following attributes:$/ do |type, name, table|
|
60
|
+
item = Then %Q{there should be a #{type} "#{name}"}
|
61
|
+
table.rows_hash.each do |key, value|
|
62
|
+
item.send(key).to_s.should == value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
When /^I delete the (contact|project|client|task|user|expense category) "([^\"]*)"$/ do |type, name|
|
67
|
+
item = Then %Q{there should be a #{type} "#{name}"}
|
68
|
+
id = harvest_api.send(pluralize(type)).delete(item)
|
69
|
+
id.should == item.id
|
70
|
+
end
|
71
|
+
|
72
|
+
Then /^the (client|project|user) "([^\"]*)" should be activated$/ do |type, identifier|
|
73
|
+
item = Then %Q{there should be a #{type} "#{identifier}"}
|
74
|
+
item.should be_active
|
75
|
+
end
|
76
|
+
|
77
|
+
Then /^the (client|project|user) "([^\"]*)" should be deactivated$/ do |type, identifier|
|
78
|
+
item = Then %Q{there should be a #{type} "#{identifier}"}
|
79
|
+
item.should_not be_active
|
80
|
+
end
|
81
|
+
|
82
|
+
When /^I deactivate the (client|project|user) "([^\"]*)"$/ do |type, identifier|
|
83
|
+
item = Then %Q{there should be a #{type} "#{identifier}"}
|
84
|
+
harvest_api.send(pluralize(type)).deactivate(item)
|
85
|
+
end
|
86
|
+
|
87
|
+
When /^I activate the (client|project|user) "([^\"]*)"$/ do |type, identifier|
|
88
|
+
item = Then %Q{there should be a #{type} "#{identifier}"}
|
89
|
+
harvest_api.send(pluralize(type)).activate(item)
|
90
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
When /^I run a project report for "([^\"]*)" for "([^\"]*)" and "([^\"]*)" the following entries should be returned:$/ do |project_name, start_date, end_date, table|
|
2
|
+
start_date = Time.parse(start_date)
|
3
|
+
end_date = Time.parse(end_date)
|
4
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
5
|
+
entries = harvest_api.reports.time_by_project(project, start_date, end_date)
|
6
|
+
table.raw.each do |row|
|
7
|
+
entry = entries.detect {|e| e.notes == row.first }
|
8
|
+
entry.should_not be_nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I run a project report for "([^\"]*)" for "([^\"]*)" and "([^\"]*)" the following entries should not be returned:$/ do |project_name, start_date, end_date, table|
|
13
|
+
start_date = Time.parse(start_date)
|
14
|
+
end_date = Time.parse(end_date)
|
15
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
16
|
+
entries = harvest_api.reports.time_by_project(project, start_date, end_date)
|
17
|
+
table.raw.each do |row|
|
18
|
+
entry = entries.detect {|e| e.notes == row.first }
|
19
|
+
entry.should be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^I run a a project report for "([^\"]*)" for "([^\"]*)" and "([^\"]*)" filtered by my user the following entries should be returned:$/ do |project_name, start_date, end_date, table|
|
24
|
+
start_date = Time.parse(start_date)
|
25
|
+
end_date = Time.parse(end_date)
|
26
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
27
|
+
user = Then %Q{there should be a user "#{@username}"}
|
28
|
+
entries = harvest_api.reports.time_by_project(project, start_date, end_date, user)
|
29
|
+
table.raw.each do |row|
|
30
|
+
entry = entries.detect {|e| e.notes == row.first }
|
31
|
+
entry.should_not be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
When /^I run a a project report for "([^\"]*)" for "([^\"]*)" and "([^\"]*)" filtered by my user the following entries should not be returned:$/ do |project_name, start_date, end_date, table|
|
36
|
+
start_date = Time.parse(start_date)
|
37
|
+
end_date = Time.parse(end_date)
|
38
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
39
|
+
user = Then %Q{there should be a user "#{@username}"}
|
40
|
+
entries = harvest_api.reports.time_by_project(project, start_date, end_date, user)
|
41
|
+
table.raw.each do |row|
|
42
|
+
entry = entries.detect {|e| e.notes == row.first }
|
43
|
+
entry.should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
When /^I run a people report for my user for "([^\"]*)" and "([^\"]*)" the following entries should be returned:$/ do |start_date, end_date, table|
|
48
|
+
start_date = Time.parse(start_date)
|
49
|
+
end_date = Time.parse(end_date)
|
50
|
+
user = Then %Q{there should be a user "#{@username}"}
|
51
|
+
entries = harvest_api.reports.time_by_user(user, start_date, end_date)
|
52
|
+
table.raw.each do |row|
|
53
|
+
entry = entries.detect {|e| e.notes == row.first }
|
54
|
+
entry.should_not be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
When /^I run a people report for my user for "([^\"]*)" and "([^\"]*)" filtered by the project "([^\"]*)" the following entries should be returned:$/ do |start_date, end_date, project_name, table|
|
59
|
+
start_date = Time.parse(start_date)
|
60
|
+
end_date = Time.parse(end_date)
|
61
|
+
user = Then %Q{there should be a user "#{@username}"}
|
62
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
63
|
+
entries = harvest_api.reports.time_by_user(user, start_date, end_date, project)
|
64
|
+
table.raw.each do |row|
|
65
|
+
entry = entries.detect {|e| e.notes == row.first }
|
66
|
+
entry.should_not be_nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
When /^I run a people report for my user for "([^\"]*)" and "([^\"]*)" filtered by the project "([^\"]*)" the following entries should not be returned:$/ do |start_date, end_date, project_name, table|
|
71
|
+
start_date = Time.parse(start_date)
|
72
|
+
end_date = Time.parse(end_date)
|
73
|
+
user = Then %Q{there should be a user "#{@username}"}
|
74
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
75
|
+
entries = harvest_api.reports.time_by_user(user, start_date, end_date, project)
|
76
|
+
table.raw.each do |row|
|
77
|
+
entry = entries.detect {|e| e.notes == row.first }
|
78
|
+
entry.should be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
When /^I run an expense report for my user for "([^\"]*)" and "([^\"]*)" the following entries should be returned:$/ do |start_date, end_date, table|
|
83
|
+
start_date = Time.parse(start_date)
|
84
|
+
end_date = Time.parse(end_date)
|
85
|
+
user = Then %Q{there should be a user "#{@username}"}
|
86
|
+
expenses = harvest_api.reports.expenses_by_user(user, start_date, end_date)
|
87
|
+
table.raw.each do |row|
|
88
|
+
expense = expenses.detect {|e| e.notes == row.first }
|
89
|
+
expense.should_not be_nil
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
When 'I create a time entry for the project "$1" and the task "$2" with the following:' do |project_name, task_name, table|
|
2
|
+
task = Then %Q{there should be a task "#{task_name}"}
|
3
|
+
project = Then %Q{there should be a project "#{project_name}"}
|
4
|
+
entry = Harvest::TimeEntry.new(table.rows_hash.merge('project_id' => project.id, 'task_id' => task.id))
|
5
|
+
created_entry = harvest_api.time.create(entry)
|
6
|
+
created_entry.project.should == project.name
|
7
|
+
created_entry.task.should == task.name
|
8
|
+
end
|
9
|
+
|
10
|
+
Then 'there should be a time entry "$1" on "$2"' do |notes, date|
|
11
|
+
entries = harvest_api.time.all(date)
|
12
|
+
entry = entries.detect {|e| e.notes == notes}
|
13
|
+
entry.should_not be_nil
|
14
|
+
entry
|
15
|
+
end
|
16
|
+
|
17
|
+
Then 'there should not be a time entry "$1" on "$2"' do |notes, date|
|
18
|
+
entries = harvest_api.time.all(date)
|
19
|
+
entry = entries.detect {|e| e.notes == notes}
|
20
|
+
entry.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
When 'I update the time entry "$1" on "$2" with the following:' do |note, date, table|
|
24
|
+
entry = Then %Q{there should be a time entry "#{note}" on "#{date}"}
|
25
|
+
entry.attributes = table.rows_hash
|
26
|
+
harvest_api.time.update(entry)
|
27
|
+
end
|
28
|
+
|
29
|
+
Then 'the time entry "$1" on "$2" should have the following attributes:' do |note, date, table|
|
30
|
+
entry = Then %Q{there should be a time entry "#{note}" on "#{date}"}
|
31
|
+
table.rows_hash.each do |key, value|
|
32
|
+
entry.send(key).to_s.should == value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
When 'I delete the time entry "$1" on "$2"' do |note, date|
|
37
|
+
entry = Then %Q{there should be a time entry "#{note}" on "#{date}"}
|
38
|
+
id = harvest_api.time.delete(entry)
|
39
|
+
entry.id.should == id
|
40
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
require 'harvested'
|
3
|
+
require 'ruby-debug'
|
4
|
+
require 'fakeweb'
|
5
|
+
|
6
|
+
require 'spec/expectations'
|
7
|
+
|
8
|
+
Before do
|
9
|
+
FakeWeb.clean_registry
|
10
|
+
FakeWeb.allow_net_connect = true
|
11
|
+
end
|
12
|
+
|
13
|
+
Before('@disconnected') do
|
14
|
+
FakeWeb.allow_net_connect = false
|
15
|
+
end
|
16
|
+
|
17
|
+
Before('@clean') do
|
18
|
+
credentials = YAML.load_file("#{File.dirname(__FILE__)}/harvest_credentials.yml")
|
19
|
+
api = Harvest.hardy_client(credentials["subdomain"], credentials["username"], credentials["password"], :ssl => credentials["ssl"])
|
20
|
+
|
21
|
+
api.users.all.each do |u|
|
22
|
+
api.users.delete(u) if u.email != credentials["username"]
|
23
|
+
end
|
24
|
+
my_user = api.users.all.first
|
25
|
+
|
26
|
+
api.reports.time_by_user(my_user, Time.parse('01/01/2000'), Time.now).each do |time|
|
27
|
+
api.time.delete(time)
|
28
|
+
end
|
29
|
+
|
30
|
+
api.reports.expenses_by_user(my_user, Time.parse('01/01/2000'), Time.now).each do |time|
|
31
|
+
api.expenses.delete(time)
|
32
|
+
end
|
33
|
+
|
34
|
+
%w(expenses expense_categories projects contacts clients tasks).each do |collection|
|
35
|
+
api.send(collection).all.each {|m| api.send(collection).delete(m) }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ErrorHelpers
|
2
|
+
def set_time_and_return_and_error
|
3
|
+
@time = Time.now
|
4
|
+
@error = nil
|
5
|
+
begin
|
6
|
+
@clients = yield
|
7
|
+
rescue => e
|
8
|
+
case e
|
9
|
+
when Harvest::HTTPError, Net::HTTPError, Net::HTTPFatalError
|
10
|
+
@error = e
|
11
|
+
else
|
12
|
+
raise e
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
World(ErrorHelpers)
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<hash>
|
3
|
+
<last-access-at type="datetime">2010-04-02T01:34:59+00:00</last-access-at>
|
4
|
+
<count type="integer">41</count>
|
5
|
+
<timeframe-limit type="integer">15</timeframe-limit>
|
6
|
+
<max-calls type="integer">40</max-calls>
|
7
|
+
<lockout-seconds type="integer">5</lockout-seconds>
|
8
|
+
</hash>
|
Binary file
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<hash>
|
3
|
+
<last-access-at type="datetime">2010-04-02T01:34:59+00:00</last-access-at>
|
4
|
+
<count type="integer">2</count>
|
5
|
+
<timeframe-limit type="integer">15</timeframe-limit>
|
6
|
+
<max-calls type="integer">40</max-calls>
|
7
|
+
<lockout-seconds type="integer">5</lockout-seconds>
|
8
|
+
</hash>
|