togglv8-tfl 0.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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +16 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +4 -0
- data/.rdoc_options +23 -0
- data/.rspec +1 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +101 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +160 -0
- data/Rakefile +6 -0
- data/lib/logging.rb +38 -0
- data/lib/reportsv2.rb +187 -0
- data/lib/togglv8.rb +13 -0
- data/lib/togglv8/clients.rb +37 -0
- data/lib/togglv8/connection.rb +99 -0
- data/lib/togglv8/dashboard.rb +14 -0
- data/lib/togglv8/project_users.rb +32 -0
- data/lib/togglv8/projects.rb +112 -0
- data/lib/togglv8/tags.rb +25 -0
- data/lib/togglv8/tasks.rb +55 -0
- data/lib/togglv8/time_entries.rb +93 -0
- data/lib/togglv8/togglv8.rb +55 -0
- data/lib/togglv8/users.rb +74 -0
- data/lib/togglv8/version.rb +4 -0
- data/lib/togglv8/workspaces.rb +47 -0
- data/spec/lib/reportsv2_spec.rb +254 -0
- data/spec/lib/togglv8/clients_spec.rb +145 -0
- data/spec/lib/togglv8/dashboard_spec.rb +31 -0
- data/spec/lib/togglv8/projects_spec.rb +111 -0
- data/spec/lib/togglv8/tags_spec.rb +54 -0
- data/spec/lib/togglv8/tasks_spec.rb +100 -0
- data/spec/lib/togglv8/time_entries_spec.rb +425 -0
- data/spec/lib/togglv8/users_spec.rb +82 -0
- data/spec/lib/togglv8/workspaces_spec.rb +40 -0
- data/spec/lib/togglv8_spec.rb +77 -0
- data/spec/spec_helper.rb +85 -0
- data/spec/togglv8_spec_helper.rb +75 -0
- data/togglv8.gemspec +34 -0
- metadata +251 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
describe 'Dashboard' do
|
2
|
+
before :all do
|
3
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
|
+
@workspaces = @toggl.workspaces
|
5
|
+
@workspace_id = @workspaces.first['id']
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'gets nil dashboard data' do
|
9
|
+
dashboard = @toggl.dashboard(@workspace_id)
|
10
|
+
expect(dashboard).to eq Hash['most_active_user' => nil, 'activity' => nil]
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'gets dashboard time entries' do
|
14
|
+
before :all do
|
15
|
+
@new_time_entry = @toggl.start_time_entry({ 'wid' => @workspace_id, 'description' => 'new time entry +1' })
|
16
|
+
end
|
17
|
+
|
18
|
+
after :all do
|
19
|
+
@toggl.delete_time_entry(@new_time_entry['id'])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'gets dashboard data' do
|
23
|
+
dashboard = @toggl.dashboard(@workspace_id)
|
24
|
+
expect(dashboard['most_active_user']).to be nil
|
25
|
+
expect(dashboard['activity']).to_not be nil
|
26
|
+
expect(dashboard['activity'].first['user_id']).to eq @toggl.me['id']
|
27
|
+
expect(dashboard['activity'].first['project_id']).to be nil
|
28
|
+
expect(dashboard['activity'].first['description']).to eq 'new time entry +1'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
describe 'Projects' do
|
2
|
+
before :all do
|
3
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
|
+
@workspaces = @toggl.workspaces
|
5
|
+
@workspace_id = @workspaces.first['id']
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'gets {} if there are no workspace projects' do
|
9
|
+
projects = @toggl.projects(@workspace_id)
|
10
|
+
expect(projects).to be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'new project' do
|
14
|
+
before :all do
|
15
|
+
@project = @toggl.create_project({ 'name' => 'new project +1', 'wid' => @workspace_id })
|
16
|
+
project_ids = @toggl.my_projects.map { |p| p['id'] }
|
17
|
+
expect(project_ids).to eq [ @project['id'] ]
|
18
|
+
end
|
19
|
+
|
20
|
+
after :all do
|
21
|
+
TogglV8SpecHelper.delete_all_projects(@toggl)
|
22
|
+
projects = @toggl.my_projects
|
23
|
+
expect(projects).to be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'creates a project' do
|
27
|
+
expect(@project).to_not be nil
|
28
|
+
expect(@project['name']).to eq 'new project +1'
|
29
|
+
expect(@project['billable']).to eq false
|
30
|
+
expect(@project['is_private']).to eq true
|
31
|
+
expect(@project['active']).to eq true
|
32
|
+
expect(@project['template']).to eq false
|
33
|
+
expect(@project['auto_estimates']).to eq false
|
34
|
+
expect(@project['wid']).to eq @workspace_id
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'gets project data' do
|
38
|
+
project = @toggl.get_project(@project['id'])
|
39
|
+
expect(project).to_not be nil
|
40
|
+
expect(project['wid']).to eq @project['wid']
|
41
|
+
expect(project['name']).to eq @project['name']
|
42
|
+
expect(project['billable']).to eq @project['billable']
|
43
|
+
expect(project['is_private']).to eq @project['is_private']
|
44
|
+
expect(project['active']).to eq @project['active']
|
45
|
+
expect(project['template']).to eq @project['template']
|
46
|
+
expect(project['auto_estimates']).to eq @project['auto_estimates']
|
47
|
+
expect(project['at']).to_not be nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'gets project users' do
|
51
|
+
users = @toggl.get_project_users(@project['id'])
|
52
|
+
expect(users.length).to eq 1
|
53
|
+
expect(users.first['uid']).to eq Testing::USER_ID
|
54
|
+
expect(users.first['manager']).to eq true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'updated project' do
|
59
|
+
before :each do
|
60
|
+
@project = @toggl.create_project({ 'name' => 'project to update', 'wid' => @workspace_id })
|
61
|
+
end
|
62
|
+
|
63
|
+
after :each do
|
64
|
+
@toggl.delete_project(@project['id'])
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'updates project data' do
|
68
|
+
new_values = {
|
69
|
+
'name' => 'PROJECT-NEW',
|
70
|
+
'is_private' => false,
|
71
|
+
'active' => false,
|
72
|
+
'auto_estimates' => true,
|
73
|
+
}
|
74
|
+
project = @toggl.update_project(@project['id'], new_values)
|
75
|
+
expect(project).to include(new_values)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'updates Pro project data', :pro_account do
|
79
|
+
new_values = {
|
80
|
+
'template' => true,
|
81
|
+
'billable' => true,
|
82
|
+
}
|
83
|
+
project = @toggl.update_project(@project['id'], new_values)
|
84
|
+
expect(project).to include(new_values)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'multiple projects' do
|
89
|
+
after :all do
|
90
|
+
TogglV8SpecHelper.delete_all_projects(@toggl)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'deletes multiple projects' do
|
94
|
+
# start with no projects
|
95
|
+
expect(@toggl.projects(@workspace_id)).to be_empty
|
96
|
+
|
97
|
+
p1 = @toggl.create_project({ 'name' => 'p1', 'wid' => @workspace_id })
|
98
|
+
p2 = @toggl.create_project({ 'name' => 'p2', 'wid' => @workspace_id })
|
99
|
+
p3 = @toggl.create_project({ 'name' => 'p3', 'wid' => @workspace_id })
|
100
|
+
|
101
|
+
# see 3 new projects
|
102
|
+
expect(@toggl.projects(@workspace_id).length).to eq 3
|
103
|
+
|
104
|
+
p_ids = [p1, p2, p3].map { |p| p['id'] }
|
105
|
+
@toggl.delete_projects(p_ids)
|
106
|
+
|
107
|
+
# end with no projects
|
108
|
+
expect(@toggl.projects(@workspace_id)).to be_empty
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
describe 'Tags' do
|
2
|
+
before :all do
|
3
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
|
+
@workspaces = @toggl.workspaces
|
5
|
+
@workspace_id = @workspaces.first['id']
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'new tag' do
|
9
|
+
before :all do
|
10
|
+
@tag = @toggl.create_tag({ 'name' => 'new tag +1', 'wid' => @workspace_id })
|
11
|
+
tag_ids = @toggl.my_tags.map { |t| t['id'] }
|
12
|
+
expect(tag_ids).to eq [ @tag['id'] ]
|
13
|
+
end
|
14
|
+
|
15
|
+
after :all do
|
16
|
+
TogglV8SpecHelper.delete_all_tags(@toggl)
|
17
|
+
tags = @toggl.my_tags
|
18
|
+
expect(tags).to be_empty
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'creates a tag' do
|
22
|
+
expect(@tag).to_not be nil
|
23
|
+
expect(@tag['name']).to eq 'new tag +1'
|
24
|
+
expect(@tag['notes']).to eq nil
|
25
|
+
expect(@tag['wid']).to eq @workspace_id
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns tag associated with workspace_id' do
|
29
|
+
tags = @toggl.tags(@workspace_id)
|
30
|
+
expect(tags).not_to be_empty
|
31
|
+
expect(tags.first['name']).to eq 'new tag +1'
|
32
|
+
expect(tags.first['wid']).to eq @workspace_id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'updated tag' do
|
37
|
+
before :each do
|
38
|
+
@tag = @toggl.create_tag({ 'name' => 'tag to update', 'wid' => @workspace_id })
|
39
|
+
end
|
40
|
+
|
41
|
+
after :each do
|
42
|
+
@toggl.delete_tag(@tag['id'])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'updates tag data' do
|
46
|
+
new_values = {
|
47
|
+
'name' => 'TAG-NEW',
|
48
|
+
}
|
49
|
+
|
50
|
+
tag = @toggl.update_tag(@tag['id'], new_values)
|
51
|
+
expect(tag).to include(new_values)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
describe 'Tasks', :pro_account do
|
2
|
+
before :all do
|
3
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
|
+
@workspaces = @toggl.workspaces
|
5
|
+
@workspace_id = @workspaces.first['id']
|
6
|
+
@project = @toggl.create_project({ 'name' => 'project with a task', 'wid' => @workspace_id })
|
7
|
+
end
|
8
|
+
|
9
|
+
after :all do
|
10
|
+
@toggl.delete_project(@project['id'])
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'new task' do
|
14
|
+
before :all do
|
15
|
+
@task = @toggl.create_task({ 'name' => 'new task +1', 'pid' => @project['id'] })
|
16
|
+
@task_ids = @toggl.get_project_tasks(@project['id']).map { |t| t['id'] }
|
17
|
+
expect(@task_ids).to eq [ @task['id'] ]
|
18
|
+
end
|
19
|
+
|
20
|
+
after :all do
|
21
|
+
@toggl.delete_tasks(@task_ids)
|
22
|
+
tasks = @toggl.get_project_tasks(@project['id'])
|
23
|
+
expect(tasks).to be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'creates a task' do
|
27
|
+
expect(@task).to_not be nil
|
28
|
+
expect(@task['name']).to eq 'new task +1'
|
29
|
+
expect(@task['pid']).to eq @project['id']
|
30
|
+
expect(@task['wid']).to eq @workspace_id
|
31
|
+
expect(@task['active']).to eq true
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'gets a task' do
|
35
|
+
task = @toggl.get_task(@task['id'])
|
36
|
+
expect(task).to include('at') # 'at' is last updated timestamp
|
37
|
+
task.delete('at') # 'at' is not included in POST response
|
38
|
+
expect(task).to eq @task # compare POST and GET responses
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'updated task' do
|
43
|
+
before :each do
|
44
|
+
@task = @toggl.create_task({ 'name' => 'task to update', 'pid' => @project['id'] })
|
45
|
+
end
|
46
|
+
|
47
|
+
after :each do
|
48
|
+
@toggl.delete_task(@task['id'])
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'updates task data' do
|
52
|
+
new_values = {
|
53
|
+
'name' => 'task-NEW',
|
54
|
+
}
|
55
|
+
|
56
|
+
task = @toggl.update_task(@task['id'], new_values)
|
57
|
+
expect(task).to include(new_values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'multiple tasks' do
|
62
|
+
before :each do
|
63
|
+
timestamp = Time.now.strftime("%H%M%S.%9N")
|
64
|
+
@task1 = @toggl.create_task({ 'name' => "task1-#{timestamp}", 'pid' => @project['id'] })
|
65
|
+
@task2 = @toggl.create_task({ 'name' => "task2-#{timestamp}", 'pid' => @project['id'] })
|
66
|
+
@task3 = @toggl.create_task({ 'name' => "task3-#{timestamp}", 'pid' => @project['id'] })
|
67
|
+
@task_ids = [ @task1['id'], @task2['id'], @task3['id'] ]
|
68
|
+
end
|
69
|
+
|
70
|
+
after :all do
|
71
|
+
@toggl.delete_tasks(@task_ids)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'updates multiple tasks' do
|
75
|
+
# start with 3 active tasks
|
76
|
+
tasks = @toggl.get_project_tasks(@project['id'])
|
77
|
+
active_flags = tasks.map { |t| t['active'] }
|
78
|
+
expect(active_flags).to match_array([true, true, true])
|
79
|
+
|
80
|
+
t_ids = [@task1, @task2, @task3].map { |t| t['id'] }
|
81
|
+
params = { 'active' => false }
|
82
|
+
@toggl.update_tasks(t_ids, params)
|
83
|
+
|
84
|
+
# end with no active tasks
|
85
|
+
tasks = @toggl.get_project_tasks(@project['id'])
|
86
|
+
expect(tasks).to be_empty
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'deletes multiple tasks' do
|
90
|
+
# start with 3 new tasks
|
91
|
+
expect(@toggl.get_project_tasks(@project['id']).length).to eq 3
|
92
|
+
|
93
|
+
t_ids = [@task1, @task2, @task3].map { |t| t['id'] }
|
94
|
+
@toggl.delete_tasks(t_ids)
|
95
|
+
|
96
|
+
# end with no active tasks
|
97
|
+
expect(@toggl.get_project_tasks(@project['id'])).to be_empty
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,425 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
describe 'Time Entries' do
|
4
|
+
before :all do
|
5
|
+
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
6
|
+
@workspaces = @toggl.workspaces
|
7
|
+
@workspace_id = @workspaces.first['id']
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'CRUD time entry' do
|
11
|
+
before :each do
|
12
|
+
time_entry_info = {
|
13
|
+
'wid' => @workspace_id,
|
14
|
+
'start' => @toggl.iso8601(DateTime.now),
|
15
|
+
'duration' => 77
|
16
|
+
}
|
17
|
+
|
18
|
+
@expected = time_entry_info.clone
|
19
|
+
|
20
|
+
@time_entry = @toggl.create_time_entry(time_entry_info)
|
21
|
+
end
|
22
|
+
|
23
|
+
after :each do
|
24
|
+
@toggl.delete_time_entry(@time_entry['id'])
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'creates a time entry' do
|
28
|
+
expect(@time_entry).to include(@expected)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'requires a workspace, project, or task to create' do
|
32
|
+
time_entry_info = {
|
33
|
+
'start' => @toggl.iso8601(DateTime.now),
|
34
|
+
'duration' => 77
|
35
|
+
}
|
36
|
+
|
37
|
+
expect {
|
38
|
+
@toggl.create_time_entry(time_entry_info)
|
39
|
+
}.to raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'gets a time entry' do
|
43
|
+
retrieved_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
44
|
+
|
45
|
+
['start', 'stop'].each do |key|
|
46
|
+
expect(retrieved_time_entry[key]).to eq_ts @time_entry[key]
|
47
|
+
retrieved_time_entry.delete(key)
|
48
|
+
@time_entry.delete(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(retrieved_time_entry).to eq @time_entry
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'updates a time entry' do
|
55
|
+
time_entry_info = {
|
56
|
+
'start' => '2010-02-13T23:31:30+00:00',
|
57
|
+
'duration' => 42
|
58
|
+
}
|
59
|
+
|
60
|
+
expected = time_entry_info.clone
|
61
|
+
|
62
|
+
time_entry_updated = @toggl.update_time_entry(@time_entry['id'], time_entry_info)
|
63
|
+
expect(time_entry_updated).to include(expected)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'deletes a time entry' do
|
67
|
+
existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
68
|
+
expect(existing_time_entry.has_key?('server_deleted_at')).to eq false
|
69
|
+
|
70
|
+
deleted_time_entry = @toggl.delete_time_entry(@time_entry['id'])
|
71
|
+
expect(deleted_time_entry).to eq "[#{ @time_entry['id'] }]"
|
72
|
+
|
73
|
+
zombie_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
74
|
+
expect(zombie_time_entry.has_key?('server_deleted_at')).to eq true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context '+ UTC offset' do
|
79
|
+
# ISO8601 times with positive '+' UTC offsets must be properly encoded
|
80
|
+
|
81
|
+
before :each do
|
82
|
+
time_entry_info = {
|
83
|
+
'wid' => @workspace_id,
|
84
|
+
'start' => '2016-01-22T12:08:14+02:00',
|
85
|
+
'duration' => 77
|
86
|
+
}
|
87
|
+
|
88
|
+
@expected = time_entry_info.clone
|
89
|
+
|
90
|
+
@time_entry = @toggl.create_time_entry(time_entry_info)
|
91
|
+
end
|
92
|
+
|
93
|
+
after :each do
|
94
|
+
@toggl.delete_time_entry(@time_entry['id'])
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'creates a time entry' do
|
98
|
+
expect(@time_entry).to include(@expected)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'requires a workspace, project, or task to create' do
|
102
|
+
time_entry_info = {
|
103
|
+
'start' => '2016-01-22T12:08:14+02:00',
|
104
|
+
'duration' => 77
|
105
|
+
}
|
106
|
+
|
107
|
+
expect {
|
108
|
+
@toggl.create_time_entry(time_entry_info)
|
109
|
+
}.to raise_error(ArgumentError)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'gets a time entry' do
|
113
|
+
retrieved_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
114
|
+
|
115
|
+
['start', 'stop'].each do |key|
|
116
|
+
expect(retrieved_time_entry[key]).to eq_ts @time_entry[key]
|
117
|
+
retrieved_time_entry.delete(key)
|
118
|
+
@time_entry.delete(key)
|
119
|
+
end
|
120
|
+
|
121
|
+
expect(retrieved_time_entry).to eq @time_entry
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'updates a time entry' do
|
125
|
+
time_entry_info = {
|
126
|
+
'start' => '2010-02-13T23:31:30+07:00',
|
127
|
+
'duration' => 42
|
128
|
+
}
|
129
|
+
|
130
|
+
expected = time_entry_info.clone
|
131
|
+
|
132
|
+
time_entry_updated = @toggl.update_time_entry(@time_entry['id'], time_entry_info)
|
133
|
+
expect(time_entry_updated).to include(expected)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'deletes a time entry' do
|
137
|
+
existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
138
|
+
expect(existing_time_entry.has_key?('server_deleted_at')).to eq false
|
139
|
+
|
140
|
+
deleted_time_entry = @toggl.delete_time_entry(@time_entry['id'])
|
141
|
+
expect(deleted_time_entry).to eq "[#{ @time_entry['id'] }]"
|
142
|
+
|
143
|
+
zombie_time_entry = @toggl.get_time_entry(@time_entry['id'])
|
144
|
+
expect(zombie_time_entry.has_key?('server_deleted_at')).to eq true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'multiple time entries' do
|
149
|
+
before :all do
|
150
|
+
time_entry_info = {
|
151
|
+
'wid' => @workspace_id,
|
152
|
+
'duration' => 77
|
153
|
+
}
|
154
|
+
@now = DateTime.now
|
155
|
+
|
156
|
+
start = { 'start' => @toggl.iso8601(@now - 9) }
|
157
|
+
@time_entry_nine_days_ago = @toggl.create_time_entry(time_entry_info.merge(start))
|
158
|
+
@nine_days_ago_id = @time_entry_nine_days_ago['id']
|
159
|
+
|
160
|
+
start = { 'start' => @toggl.iso8601(@now - 7) }
|
161
|
+
@time_entry_last_week = @toggl.create_time_entry(time_entry_info.merge(start))
|
162
|
+
@last_week_id = @time_entry_last_week['id']
|
163
|
+
|
164
|
+
start = { 'start' => @toggl.iso8601(@now) }
|
165
|
+
@time_entry_now = @toggl.create_time_entry(time_entry_info.merge(start))
|
166
|
+
@now_id = @time_entry_now['id']
|
167
|
+
|
168
|
+
start = { 'start' => @toggl.iso8601(@now + 7) }
|
169
|
+
@time_entry_next_week = @toggl.create_time_entry(time_entry_info.merge(start))
|
170
|
+
@next_week_id = @time_entry_next_week['id']
|
171
|
+
end
|
172
|
+
|
173
|
+
after :all do
|
174
|
+
TogglV8SpecHelper.delete_all_time_entries(@toggl)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'gets time entries (reaching back 9 days up till now)' do
|
178
|
+
ids = @toggl.get_time_entries.map { |t| t['id']}
|
179
|
+
expect(ids).to eq [ @last_week_id, @now_id ]
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'gets time entries after start_date (up till now)' do
|
183
|
+
ids = @toggl.get_time_entries({:start_date => @now - 1}).map { |t| t['id']}
|
184
|
+
expect(ids).to eq [ @now_id ]
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'gets time entries between start_date and end_date' do
|
188
|
+
ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 1}).map { |t| t['id']}
|
189
|
+
expect(ids).to eq [ @now_id ]
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'gets time entries in the future' do
|
193
|
+
ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 8}).map { |t| t['id']}
|
194
|
+
expect(ids).to eq [ @now_id, @next_week_id ]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'start and stop time entry' do
|
199
|
+
it 'starts and stops a time entry' do
|
200
|
+
time_entry_info = {
|
201
|
+
'wid' => @workspace_id,
|
202
|
+
'description' => 'time entry description'
|
203
|
+
}
|
204
|
+
|
205
|
+
# start time entry
|
206
|
+
running_time_entry = @toggl.start_time_entry(time_entry_info)
|
207
|
+
|
208
|
+
# get current time entry by '/current'
|
209
|
+
time_entry_current = @toggl.get_current_time_entry
|
210
|
+
# get current time entry by id
|
211
|
+
time_entry_by_id = @toggl.get_time_entry(running_time_entry['id'])
|
212
|
+
|
213
|
+
# compare two methods of getting current time entry
|
214
|
+
expect(time_entry_current).to eq time_entry_by_id
|
215
|
+
|
216
|
+
# compare current time entry with running time entry
|
217
|
+
expect(time_entry_by_id['start']).to eq_ts running_time_entry['start']
|
218
|
+
time_entry_by_id.delete('start')
|
219
|
+
running_time_entry.delete('start')
|
220
|
+
|
221
|
+
expect(time_entry_by_id).to eq running_time_entry
|
222
|
+
expect(time_entry_by_id.has_key?('stop')).to eq false
|
223
|
+
|
224
|
+
# stop time entry
|
225
|
+
stopped_time_entry = @toggl.stop_time_entry(running_time_entry['id'])
|
226
|
+
expect(stopped_time_entry.has_key?('stop')).to eq true
|
227
|
+
|
228
|
+
@toggl.delete_time_entry(stopped_time_entry['id'])
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'returns nil if there is no current time entry' do
|
232
|
+
time_entry = @toggl.get_current_time_entry
|
233
|
+
expect(time_entry).to be nil
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'requires a workspace, project, or task to start' do
|
237
|
+
time_entry_info = {
|
238
|
+
'description' => 'time entry description'
|
239
|
+
}
|
240
|
+
|
241
|
+
expect {
|
242
|
+
@toggl.start_time_entry(time_entry_info)
|
243
|
+
}.to raise_error(ArgumentError)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
context 'time entry tags' do
|
248
|
+
before :each do
|
249
|
+
time_entry_info = {
|
250
|
+
'wid' => @workspace_id,
|
251
|
+
'duration' => 7777
|
252
|
+
}
|
253
|
+
@now = DateTime.now
|
254
|
+
|
255
|
+
start = { 'start' => @toggl.iso8601(@now - 7) }
|
256
|
+
@time7 = @toggl.create_time_entry(time_entry_info.merge(start))
|
257
|
+
|
258
|
+
start = { 'start' => @toggl.iso8601(@now - 6) }
|
259
|
+
@time6 = @toggl.create_time_entry(time_entry_info.merge(start))
|
260
|
+
|
261
|
+
start = { 'start' => @toggl.iso8601(@now - 5) }
|
262
|
+
@time5 = @toggl.create_time_entry(time_entry_info.merge(start))
|
263
|
+
|
264
|
+
start = { 'start' => @toggl.iso8601(@now - 4) }
|
265
|
+
@time4 = @toggl.create_time_entry(time_entry_info.merge(start))
|
266
|
+
|
267
|
+
@time_entry_ids = [ @time7['id'], @time6['id'], @time5['id'], @time4['id']]
|
268
|
+
end
|
269
|
+
|
270
|
+
after :each do
|
271
|
+
TogglV8SpecHelper.delete_all_time_entries(@toggl)
|
272
|
+
TogglV8SpecHelper.delete_all_tags(@toggl)
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'adds and removes one tag' do
|
276
|
+
# Add one tag
|
277
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
278
|
+
{'tags' =>['money'], 'tag_action' => 'add'})
|
279
|
+
|
280
|
+
time_entries = @toggl.get_time_entries
|
281
|
+
tags = time_entries.map { |t| t['tags'] }
|
282
|
+
expect(tags).to eq [
|
283
|
+
['money'],
|
284
|
+
['money'],
|
285
|
+
['money'],
|
286
|
+
['money']
|
287
|
+
]
|
288
|
+
|
289
|
+
# Remove one tag
|
290
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
291
|
+
{'tags' =>['money'], 'tag_action' => 'remove'})
|
292
|
+
|
293
|
+
time_entries = @toggl.get_time_entries
|
294
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
295
|
+
expect(tags).to eq []
|
296
|
+
end
|
297
|
+
|
298
|
+
it '"removes" a non-existent tag' do
|
299
|
+
# Not tags to start
|
300
|
+
time_entries = @toggl.get_time_entries
|
301
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
302
|
+
expect(tags).to eq []
|
303
|
+
|
304
|
+
# "Remove" a tag
|
305
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
306
|
+
{'tags' =>['void'], 'tag_action' => 'remove'})
|
307
|
+
|
308
|
+
# No tags to finish
|
309
|
+
time_entries = @toggl.get_time_entries
|
310
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
311
|
+
expect(tags).to eq []
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'adds and removes multiple tags' do
|
315
|
+
# Add multiple tags
|
316
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
317
|
+
{'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
|
318
|
+
|
319
|
+
time_entries = @toggl.get_time_entries
|
320
|
+
tags = time_entries.map { |t| t['tags'] }
|
321
|
+
expect(tags).to eq [
|
322
|
+
['billed', 'productive'],
|
323
|
+
['billed', 'productive'],
|
324
|
+
['billed', 'productive'],
|
325
|
+
['billed', 'productive']
|
326
|
+
]
|
327
|
+
|
328
|
+
# Remove multiple tags
|
329
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
330
|
+
{'tags' =>['billed','productive'], 'tag_action' => 'remove'})
|
331
|
+
|
332
|
+
time_entries = @toggl.get_time_entries
|
333
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
334
|
+
expect(tags).to eq []
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'manages multiple tags' do
|
338
|
+
# Add some tags
|
339
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
340
|
+
{'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
|
341
|
+
|
342
|
+
# Remove some tags
|
343
|
+
@toggl.update_time_entries_tags([ @time6['id'], @time4['id'] ],
|
344
|
+
{'tags' =>['billed'], 'tag_action' => 'remove'})
|
345
|
+
|
346
|
+
# Add some tags
|
347
|
+
@toggl.update_time_entries_tags([ @time7['id'] ],
|
348
|
+
{'tags' =>['best'], 'tag_action' => 'add'})
|
349
|
+
|
350
|
+
time7 = @toggl.get_time_entry(@time7['id'])
|
351
|
+
time6 = @toggl.get_time_entry(@time6['id'])
|
352
|
+
time5 = @toggl.get_time_entry(@time5['id'])
|
353
|
+
time4 = @toggl.get_time_entry(@time4['id'])
|
354
|
+
|
355
|
+
tags = [ time7['tags'], time6['tags'], time5['tags'], time4['tags'] ]
|
356
|
+
expect(tags).to eq [
|
357
|
+
['best', 'billed', 'productive'],
|
358
|
+
[ 'productive'],
|
359
|
+
[ 'billed', 'productive'],
|
360
|
+
[ 'productive']
|
361
|
+
]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
context 'iso8601' do
|
366
|
+
before :all do
|
367
|
+
@ts = DateTime.new(2008,6,21, 13,30,2, "+09:00")
|
368
|
+
@expected = '2008-06-21T13:30:02+09:00'
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'formats a DateTime' do
|
372
|
+
expect(@toggl.iso8601(@ts)).to eq @expected
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'formats a Date' do
|
376
|
+
ts = @ts.to_date
|
377
|
+
expect(@toggl.iso8601(@ts)).to eq @expected
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'formats a Time' do
|
381
|
+
ts = @ts.to_time
|
382
|
+
expect(@toggl.iso8601(@ts)).to eq @expected
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'cannot format a FixNum' do
|
386
|
+
expect{ @toggl.iso8601(1234567890) }.to raise_error(ArgumentError)
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'cannot format a malformed timestamp' do
|
390
|
+
expect{ @toggl.iso8601('X') }.to raise_error(ArgumentError)
|
391
|
+
end
|
392
|
+
|
393
|
+
context 'String' do
|
394
|
+
it 'converts +00:00 to Zulu' do
|
395
|
+
ts = '2015-08-21T09:21:02+00:00'
|
396
|
+
expected = '2015-08-21T09:21:02Z'
|
397
|
+
|
398
|
+
expect(@toggl.iso8601(ts)).to eq expected
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'converts -00:00 to Z' do
|
402
|
+
ts = '2015-08-21T09:21:02-00:00'
|
403
|
+
expected = '2015-08-21T09:21:02Z'
|
404
|
+
|
405
|
+
expect(@toggl.iso8601(ts)).to eq expected
|
406
|
+
end
|
407
|
+
|
408
|
+
it 'maintains an offset' do
|
409
|
+
expect(@toggl.iso8601('2015-08-21T04:21:02-05:00')).to eq '2015-08-21T04:21:02-05:00'
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
RSpec::Matchers.define :eq_ts do |expected|
|
415
|
+
# Matching actual time is necessary due to differing formats.
|
416
|
+
# Example:
|
417
|
+
# 1) POST time_entries/start returns 2015-08-21T07:28:20Z
|
418
|
+
# when GET time_entries/{time_entry_id} returns 2015-08-21T07:28:20+00:00
|
419
|
+
# 2) 2015-08-21T03:20:30-05:00 and 2015-08-21T08:20:30+00:00 refer to
|
420
|
+
# the same moment in time, but one is in local time and the other in UTC
|
421
|
+
match do |actual|
|
422
|
+
DateTime.parse(actual) == DateTime.parse(expected)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|