togglv8-lastobelus 1.2.2

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.
@@ -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,433 @@
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
+ # As of 2019-07-08T16:40:35.573Z, POST /api/v8/time_entries does not return 'guid' as GET does
52
+ retrieved_time_entry.delete('guid')
53
+
54
+ expect(retrieved_time_entry).to eq @time_entry
55
+ end
56
+
57
+ it 'updates a time entry' do
58
+ time_entry_info = {
59
+ 'start' => '2010-02-13T23:31:30+00:00',
60
+ 'duration' => 42
61
+ }
62
+
63
+ expected = time_entry_info.clone
64
+
65
+ time_entry_updated = @toggl.update_time_entry(@time_entry['id'], time_entry_info)
66
+ expect(time_entry_updated).to include(expected)
67
+ end
68
+
69
+ it 'deletes a time entry' do
70
+ existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
71
+ expect(existing_time_entry.has_key?('server_deleted_at')).to eq false
72
+
73
+ deleted_time_entry = @toggl.delete_time_entry(@time_entry['id'])
74
+ expect(deleted_time_entry).to eq "[#{ @time_entry['id'] }]"
75
+
76
+ zombie_time_entry = @toggl.get_time_entry(@time_entry['id'])
77
+ expect(zombie_time_entry.has_key?('server_deleted_at')).to eq true
78
+ end
79
+ end
80
+
81
+ context '+ UTC offset' do
82
+ # ISO8601 times with positive '+' UTC offsets must be properly encoded
83
+
84
+ before :each do
85
+ time_entry_info = {
86
+ 'wid' => @workspace_id,
87
+ 'start' => '2016-01-22T12:08:14+02:00',
88
+ 'duration' => 77
89
+ }
90
+
91
+ @expected = time_entry_info.clone
92
+
93
+ @time_entry = @toggl.create_time_entry(time_entry_info)
94
+ end
95
+
96
+ after :each do
97
+ @toggl.delete_time_entry(@time_entry['id'])
98
+ end
99
+
100
+ it 'creates a time entry' do
101
+ expect(@time_entry).to include(@expected)
102
+ end
103
+
104
+ it 'requires a workspace, project, or task to create' do
105
+ time_entry_info = {
106
+ 'start' => '2016-01-22T12:08:14+02:00',
107
+ 'duration' => 77
108
+ }
109
+
110
+ expect {
111
+ @toggl.create_time_entry(time_entry_info)
112
+ }.to raise_error(ArgumentError)
113
+ end
114
+
115
+ it 'gets a time entry' do
116
+ retrieved_time_entry = @toggl.get_time_entry(@time_entry['id'])
117
+
118
+ ['start', 'stop'].each do |key|
119
+ expect(retrieved_time_entry[key]).to eq_ts @time_entry[key]
120
+ retrieved_time_entry.delete(key)
121
+ @time_entry.delete(key)
122
+ end
123
+
124
+ # As of 2019-07-08T16:40:35.573Z, POST /api/v8/time_entries does not return 'guid' as GET does
125
+ retrieved_time_entry.delete('guid')
126
+
127
+ expect(retrieved_time_entry).to eq @time_entry
128
+ end
129
+
130
+ it 'updates a time entry' do
131
+ time_entry_info = {
132
+ 'start' => '2010-02-13T23:31:30+07:00',
133
+ 'duration' => 42
134
+ }
135
+
136
+ expected = time_entry_info.clone
137
+
138
+ time_entry_updated = @toggl.update_time_entry(@time_entry['id'], time_entry_info)
139
+ expect(time_entry_updated).to include(expected)
140
+ end
141
+
142
+ it 'deletes a time entry' do
143
+ existing_time_entry = @toggl.get_time_entry(@time_entry['id'])
144
+ expect(existing_time_entry.has_key?('server_deleted_at')).to eq false
145
+
146
+ deleted_time_entry = @toggl.delete_time_entry(@time_entry['id'])
147
+ expect(deleted_time_entry).to eq "[#{ @time_entry['id'] }]"
148
+
149
+ zombie_time_entry = @toggl.get_time_entry(@time_entry['id'])
150
+ expect(zombie_time_entry.has_key?('server_deleted_at')).to eq true
151
+ end
152
+ end
153
+
154
+ context 'multiple time entries' do
155
+ before :all do
156
+ time_entry_info = {
157
+ 'wid' => @workspace_id,
158
+ 'duration' => 77
159
+ }
160
+ @now = DateTime.now
161
+
162
+ start = { 'start' => @toggl.iso8601(@now - 9) }
163
+ @time_entry_nine_days_ago = @toggl.create_time_entry(time_entry_info.merge(start))
164
+ @nine_days_ago_id = @time_entry_nine_days_ago['id']
165
+
166
+ start = { 'start' => @toggl.iso8601(@now - 7) }
167
+ @time_entry_last_week = @toggl.create_time_entry(time_entry_info.merge(start))
168
+ @last_week_id = @time_entry_last_week['id']
169
+
170
+ start = { 'start' => @toggl.iso8601(@now) }
171
+ @time_entry_now = @toggl.create_time_entry(time_entry_info.merge(start))
172
+ @now_id = @time_entry_now['id']
173
+
174
+ start = { 'start' => @toggl.iso8601(@now + 7) }
175
+ @time_entry_next_week = @toggl.create_time_entry(time_entry_info.merge(start))
176
+ @next_week_id = @time_entry_next_week['id']
177
+ end
178
+
179
+ after :all do
180
+ TogglV8SpecHelper.delete_all_time_entries(@toggl)
181
+ end
182
+
183
+ it 'gets time entries (reaching back 9 days up till now)' do
184
+ ids = @toggl.get_time_entries.map { |t| t['id']}
185
+ expect(ids).to eq [ @last_week_id, @now_id ]
186
+ end
187
+
188
+ it 'gets time entries after start_date (up till now)' do
189
+ ids = @toggl.get_time_entries({:start_date => @now - 1}).map { |t| t['id']}
190
+ expect(ids).to eq [ @now_id ]
191
+ end
192
+
193
+ it 'gets time entries between start_date and end_date' do
194
+ ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 1}).map { |t| t['id']}
195
+ expect(ids).to eq [ @now_id ]
196
+ end
197
+
198
+ it 'gets time entries in the future' do
199
+ ids = @toggl.get_time_entries({:start_date => @now - 1, :end_date => @now + 8}).map { |t| t['id']}
200
+ expect(ids).to eq [ @now_id, @next_week_id ]
201
+ end
202
+ end
203
+
204
+ context 'start and stop time entry' do
205
+ let(:running_time_entry) { @toggl.start_time_entry(@time_entry_info).tap { @_running_time_entry_loaded = true } }
206
+ after :each do
207
+ @toggl.delete_time_entry(running_time_entry['id']) if @_running_time_entry_loaded
208
+ end
209
+
210
+ it 'starts and stops a time entry' do
211
+ @time_entry_info = {
212
+ 'wid' => @workspace_id,
213
+ 'description' => 'time entry description'
214
+ }
215
+ running_time_entry
216
+
217
+ # get current time entry by '/current'
218
+ time_entry_current = @toggl.get_current_time_entry
219
+ # get current time entry by id
220
+ time_entry_by_id = @toggl.get_time_entry(running_time_entry['id'])
221
+
222
+ # compare two methods of getting current time entry
223
+ expect(time_entry_current).to eq time_entry_by_id
224
+
225
+ # compare current time entry with running time entry
226
+ expect(time_entry_by_id['start']).to eq_ts running_time_entry['start']
227
+ time_entry_by_id.delete('start')
228
+ running_time_entry.delete('start')
229
+
230
+ # As of 2019-07-08T16:40:35.573Z, POST /api/v8/time_entries does not return 'guid' as GET does
231
+ time_entry_by_id.delete('guid')
232
+
233
+ expect(time_entry_by_id).to eq running_time_entry
234
+ expect(time_entry_by_id.has_key?('stop')).to eq false
235
+
236
+ # stop time entry
237
+ stopped_time_entry = @toggl.stop_time_entry(running_time_entry['id'])
238
+ expect(stopped_time_entry.has_key?('stop')).to eq true
239
+ end
240
+
241
+ it 'returns nil if there is no current time entry' do
242
+ time_entry = @toggl.get_current_time_entry
243
+ expect(time_entry).to be nil
244
+ end
245
+
246
+ it 'requires a workspace, project, or task to start' do
247
+ @time_entry_info = {
248
+ 'description' => 'time entry description'
249
+ }
250
+
251
+ expect { running_time_entry }.to raise_error(ArgumentError)
252
+ end
253
+ end
254
+
255
+ context 'time entry tags' do
256
+ before :each do
257
+ time_entry_info = {
258
+ 'wid' => @workspace_id,
259
+ 'duration' => 7777
260
+ }
261
+ @now = DateTime.now
262
+
263
+ start = { 'start' => @toggl.iso8601(@now - 7) }
264
+ @time7 = @toggl.create_time_entry(time_entry_info.merge(start))
265
+
266
+ start = { 'start' => @toggl.iso8601(@now - 6) }
267
+ @time6 = @toggl.create_time_entry(time_entry_info.merge(start))
268
+
269
+ start = { 'start' => @toggl.iso8601(@now - 5) }
270
+ @time5 = @toggl.create_time_entry(time_entry_info.merge(start))
271
+
272
+ start = { 'start' => @toggl.iso8601(@now - 4) }
273
+ @time4 = @toggl.create_time_entry(time_entry_info.merge(start))
274
+
275
+ @time_entry_ids = [ @time7['id'], @time6['id'], @time5['id'], @time4['id']]
276
+ end
277
+
278
+ after :each do
279
+ TogglV8SpecHelper.delete_all_time_entries(@toggl)
280
+ TogglV8SpecHelper.delete_all_tags(@toggl)
281
+ end
282
+
283
+ it 'adds and removes one tag' do
284
+ # Add one tag
285
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
286
+ {'tags' =>['money'], 'tag_action' => 'add'})
287
+
288
+ time_entries = @toggl.get_time_entries
289
+ tags = time_entries.map { |t| t['tags'] }
290
+ expect(tags).to eq [
291
+ ['money'],
292
+ ['money'],
293
+ ['money'],
294
+ ['money']
295
+ ]
296
+
297
+ # Remove one tag
298
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
299
+ {'tags' =>['money'], 'tag_action' => 'remove'})
300
+
301
+ time_entries = @toggl.get_time_entries
302
+ tags = time_entries.map { |t| t['tags'] }.compact
303
+ expect(tags).to eq []
304
+ end
305
+
306
+ it '"removes" a non-existent tag' do
307
+ # Not tags to start
308
+ time_entries = @toggl.get_time_entries
309
+ tags = time_entries.map { |t| t['tags'] }.compact
310
+ expect(tags).to eq []
311
+
312
+ # "Remove" a tag
313
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
314
+ {'tags' =>['void'], 'tag_action' => 'remove'})
315
+
316
+ # No tags to finish
317
+ time_entries = @toggl.get_time_entries
318
+ tags = time_entries.map { |t| t['tags'] }.compact
319
+ expect(tags).to eq []
320
+ end
321
+
322
+ it 'adds and removes multiple tags' do
323
+ # Add multiple tags
324
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
325
+ {'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
326
+
327
+ time_entries = @toggl.get_time_entries
328
+ tags = time_entries.map { |t| t['tags'] }
329
+ expect(tags).to eq [
330
+ ['billed', 'productive'],
331
+ ['billed', 'productive'],
332
+ ['billed', 'productive'],
333
+ ['billed', 'productive']
334
+ ]
335
+
336
+ # Remove multiple tags
337
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
338
+ {'tags' =>['billed','productive'], 'tag_action' => 'remove'})
339
+
340
+ time_entries = @toggl.get_time_entries
341
+ tags = time_entries.map { |t| t['tags'] }.compact
342
+ expect(tags).to eq []
343
+ end
344
+
345
+ it 'manages multiple tags' do
346
+ # Add some tags
347
+ @toggl.update_time_entries_tags_fixed(@time_entry_ids,
348
+ {'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
349
+
350
+ # Remove some tags
351
+ @toggl.update_time_entries_tags_fixed([ @time6['id'], @time4['id'] ],
352
+ {'tags' =>['billed'], 'tag_action' => 'remove'})
353
+
354
+ # Add some tags
355
+ @toggl.update_time_entries_tags_fixed([ @time7['id'] ],
356
+ {'tags' =>['best'], 'tag_action' => 'add'})
357
+
358
+ time7 = @toggl.get_time_entry(@time7['id'])
359
+ time6 = @toggl.get_time_entry(@time6['id'])
360
+ time5 = @toggl.get_time_entry(@time5['id'])
361
+ time4 = @toggl.get_time_entry(@time4['id'])
362
+
363
+ tags = [ time7['tags'], time6['tags'], time5['tags'], time4['tags'] ]
364
+ expect(tags).to eq [
365
+ ['best', 'billed', 'productive'],
366
+ [ 'productive'],
367
+ [ 'billed', 'productive'],
368
+ [ 'productive']
369
+ ]
370
+ end
371
+ end
372
+
373
+ context 'iso8601' do
374
+ before :all do
375
+ @ts = DateTime.new(2008,6,21, 13,30,2, "+09:00")
376
+ @expected = '2008-06-21T13:30:02+09:00'
377
+ end
378
+
379
+ it 'formats a DateTime' do
380
+ expect(@toggl.iso8601(@ts)).to eq @expected
381
+ end
382
+
383
+ it 'formats a Date' do
384
+ ts = @ts.to_date
385
+ expect(@toggl.iso8601(@ts)).to eq @expected
386
+ end
387
+
388
+ it 'formats a Time' do
389
+ ts = @ts.to_time
390
+ expect(@toggl.iso8601(@ts)).to eq @expected
391
+ end
392
+
393
+ it 'cannot format a FixNum' do
394
+ expect{ @toggl.iso8601(1234567890) }.to raise_error(ArgumentError)
395
+ end
396
+
397
+ it 'cannot format a malformed timestamp' do
398
+ expect{ @toggl.iso8601('X') }.to raise_error(ArgumentError)
399
+ end
400
+
401
+ context 'String' do
402
+ it 'converts +00:00 to Zulu' do
403
+ ts = '2015-08-21T09:21:02+00:00'
404
+ expected = '2015-08-21T09:21:02Z'
405
+
406
+ expect(@toggl.iso8601(ts)).to eq expected
407
+ end
408
+
409
+ it 'converts -00:00 to Z' do
410
+ ts = '2015-08-21T09:21:02-00:00'
411
+ expected = '2015-08-21T09:21:02Z'
412
+
413
+ expect(@toggl.iso8601(ts)).to eq expected
414
+ end
415
+
416
+ it 'maintains an offset' do
417
+ expect(@toggl.iso8601('2015-08-21T04:21:02-05:00')).to eq '2015-08-21T04:21:02-05:00'
418
+ end
419
+ end
420
+ end
421
+
422
+ RSpec::Matchers.define :eq_ts do |expected|
423
+ # Matching actual time is necessary due to differing formats.
424
+ # Example:
425
+ # 1) POST time_entries/start returns 2015-08-21T07:28:20Z
426
+ # when GET time_entries/{time_entry_id} returns 2015-08-21T07:28:20+00:00
427
+ # 2) 2015-08-21T03:20:30-05:00 and 2015-08-21T08:20:30+00:00 refer to
428
+ # the same moment in time, but one is in local time and the other in UTC
429
+ match do |actual|
430
+ DateTime.parse(actual) == DateTime.parse(expected)
431
+ end
432
+ end
433
+ end