friends 0.28 → 0.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +13 -0
- data/README.md +35 -5
- data/Rakefile +1 -1
- data/bin/friends +7 -379
- data/friends.gemspec +3 -1
- data/lib/friends/activity.rb +4 -4
- data/lib/friends/commands/add.rb +64 -0
- data/lib/friends/commands/clean.rb +9 -0
- data/lib/friends/commands/edit.rb +12 -0
- data/lib/friends/commands/graph.rb +56 -0
- data/lib/friends/commands/list.rb +143 -0
- data/lib/friends/commands/remove.rb +27 -0
- data/lib/friends/commands/rename.rb +30 -0
- data/lib/friends/commands/set.rb +14 -0
- data/lib/friends/commands/stats.rb +11 -0
- data/lib/friends/commands/suggest.rb +20 -0
- data/lib/friends/commands/update.rb +29 -0
- data/lib/friends/graph.rb +7 -7
- data/lib/friends/introvert.rb +23 -18
- data/lib/friends/version.rb +1 -1
- data/test/commands/add/activity_spec.rb +379 -0
- data/test/commands/add/friend_spec.rb +30 -0
- data/test/commands/add/location_spec.rb +30 -0
- data/test/commands/add/nickname_spec.rb +50 -0
- data/test/commands/add/tag_spec.rb +65 -0
- data/test/commands/clean_spec.rb +39 -0
- data/test/commands/graph_spec.rb +147 -0
- data/test/commands/help_spec.rb +45 -0
- data/test/commands/list/activities_spec.rb +136 -0
- data/test/commands/list/favorite/friends_spec.rb +77 -0
- data/test/commands/list/favorite/locations_spec.rb +82 -0
- data/test/commands/list/friends_spec.rb +76 -0
- data/test/commands/list/locations_spec.rb +35 -0
- data/test/commands/list/tags_spec.rb +58 -0
- data/test/commands/remove/nickname_spec.rb +63 -0
- data/test/commands/remove/tag_spec.rb +64 -0
- data/test/commands/rename/friend_spec.rb +55 -0
- data/test/commands/rename/location_spec.rb +43 -0
- data/test/commands/set/location_spec.rb +54 -0
- data/test/commands/stats_spec.rb +41 -0
- data/test/commands/suggest_spec.rb +86 -0
- data/test/commands/update_spec.rb +13 -0
- data/test/helper.rb +114 -0
- metadata +89 -15
- data/test/activity_spec.rb +0 -597
- data/test/friend_spec.rb +0 -241
- data/test/graph_spec.rb +0 -92
- data/test/introvert_spec.rb +0 -969
- data/test/location_spec.rb +0 -60
data/lib/friends/graph.rb
CHANGED
@@ -5,13 +5,13 @@ module Friends
|
|
5
5
|
class Graph
|
6
6
|
DATE_FORMAT = "%b %Y"
|
7
7
|
|
8
|
-
# @param start_date [Date] the first month of the graph
|
9
|
-
# @param end_date [Date] the last month of the graph
|
10
8
|
# @param activities [Array<Friends::Activity>] a list of activities to graph
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
def initialize(activities:)
|
10
|
+
@activities = activities
|
11
|
+
unless @activities.empty?
|
12
|
+
@start_date = @activities.last.date
|
13
|
+
@end_date = @activities.first.date
|
14
|
+
end
|
15
15
|
end
|
16
16
|
|
17
17
|
# Render the graph as a hash in the format:
|
@@ -45,7 +45,7 @@ module Friends
|
|
45
45
|
#
|
46
46
|
# @return [Hash{String => Integer}]
|
47
47
|
def empty_graph
|
48
|
-
Hash[(start_date..end_date).map do |date|
|
48
|
+
Hash[(start_date && end_date ? (start_date..end_date) : []).map do |date|
|
49
49
|
[format_date(date), 0]
|
50
50
|
end]
|
51
51
|
end
|
data/lib/friends/introvert.rb
CHANGED
@@ -231,17 +231,21 @@ module Friends
|
|
231
231
|
# for unfiltered
|
232
232
|
# @param tagged [String] the name of a tag to filter by (of the form:
|
233
233
|
# "@tag"), or nil for unfiltered
|
234
|
+
# @param since_date [Date] a date on or after which to find activities, or nil for unfiltered
|
235
|
+
# @param until_date [Date] a date before or on which to find activities, or nil for unfiltered
|
234
236
|
# @return [Array] a list of all activity text values
|
235
237
|
# @raise [ArgumentError] if limit is present but limit < 1
|
236
238
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
237
239
|
# is ambiguous
|
238
|
-
def list_activities(limit:, with:, location_name:, tagged:)
|
240
|
+
def list_activities(limit:, with:, location_name:, tagged:, since_date:, until_date:)
|
239
241
|
raise ArgumentError, "Limit must be positive" if limit && limit < 1
|
240
242
|
|
241
243
|
acts = filtered_activities(
|
242
244
|
with: with,
|
243
245
|
location_name: location_name,
|
244
|
-
tagged: tagged
|
246
|
+
tagged: tagged,
|
247
|
+
since_date: since_date,
|
248
|
+
until_date: until_date
|
245
249
|
)
|
246
250
|
|
247
251
|
# If we need to, trim the list.
|
@@ -296,24 +300,21 @@ module Friends
|
|
296
300
|
# for unfiltered
|
297
301
|
# @param tagged [String] the name of a tag to filter by (of the form:
|
298
302
|
# "@tag"), or nil for unfiltered
|
303
|
+
# @param since_date [Date] a date on or after which to find activities, or nil for unfiltered
|
304
|
+
# @param until_date [Date] a date before or on which to find activities, or nil for unfiltered
|
299
305
|
# @return [Hash{String => Integer}]
|
300
306
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
301
307
|
# is ambiguous
|
302
|
-
def graph(with:, location_name:, tagged:)
|
303
|
-
# There is no point trying to graph no activities
|
304
|
-
return {} if @activities.empty?
|
305
|
-
|
308
|
+
def graph(with:, location_name:, tagged:, since_date:, until_date:)
|
306
309
|
activities_to_graph = filtered_activities(
|
307
310
|
with: with,
|
308
311
|
location_name: location_name,
|
309
|
-
tagged: tagged
|
312
|
+
tagged: tagged,
|
313
|
+
since_date: since_date,
|
314
|
+
until_date: until_date
|
310
315
|
)
|
311
316
|
|
312
|
-
Graph.new(
|
313
|
-
start_date: @activities.last.date,
|
314
|
-
end_date: @activities.first.date,
|
315
|
-
activities: activities_to_graph
|
316
|
-
).to_h
|
317
|
+
Graph.new(activities: activities_to_graph).to_h
|
317
318
|
end
|
318
319
|
|
319
320
|
# Suggest friends to do something with.
|
@@ -456,10 +457,12 @@ module Friends
|
|
456
457
|
# for unfiltered
|
457
458
|
# @param tagged [String] the name of a tag to filter by, or nil for
|
458
459
|
# unfiltered
|
460
|
+
# @param since_date [Date] a date on or after which to find activities, or nil for unfiltered
|
461
|
+
# @param until_date [Date] a date before or on which to find activities, or nil for unfiltered
|
459
462
|
# @return [Array] an array of activities
|
460
463
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
461
464
|
# is ambiguous
|
462
|
-
def filtered_activities(with:, location_name:, tagged:)
|
465
|
+
def filtered_activities(with:, location_name:, tagged:, since_date:, until_date:)
|
463
466
|
acts = @activities
|
464
467
|
|
465
468
|
# Filter by friend name if argument is passed.
|
@@ -475,9 +478,11 @@ module Friends
|
|
475
478
|
end
|
476
479
|
|
477
480
|
# Filter by tag if argument is passed.
|
478
|
-
unless tagged.nil?
|
479
|
-
|
480
|
-
|
481
|
+
acts = acts.select { |act| act.includes_tag?(tagged) } unless tagged.nil?
|
482
|
+
|
483
|
+
# Filter by date if arguments are passed.
|
484
|
+
acts = acts.select { |act| act.date >= since_date } unless since_date.nil?
|
485
|
+
acts = acts.select { |act| act.date <= until_date } unless until_date.nil?
|
481
486
|
|
482
487
|
acts
|
483
488
|
end
|
@@ -503,7 +508,7 @@ module Friends
|
|
503
508
|
results.map.with_index(0) do |thing, index|
|
504
509
|
name = thing.name.ljust(max_str_size)
|
505
510
|
n = thing.n_activities
|
506
|
-
if index
|
511
|
+
if index.zero?
|
507
512
|
label = n == 1 ? " activity" : " activities"
|
508
513
|
end
|
509
514
|
parenthetical = "(#{n}#{label})"
|
@@ -627,7 +632,7 @@ module Friends
|
|
627
632
|
# with that exact name, match it.
|
628
633
|
if things.size > 1
|
629
634
|
exact_things = things.select do |thing|
|
630
|
-
thing.name.casecmp(text)
|
635
|
+
thing.name.casecmp(text).zero? # We ignore case for an "exact" match.
|
631
636
|
end
|
632
637
|
|
633
638
|
things = exact_things if exact_things.size == 1
|
data/lib/friends/version.rb
CHANGED
@@ -0,0 +1,379 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "./test/helper"
|
4
|
+
|
5
|
+
def date_parsing_specs(test_stdout: true)
|
6
|
+
describe "date parsing" do
|
7
|
+
let(:description) { "Test." }
|
8
|
+
|
9
|
+
describe "when date is in YYYY-MM-DD" do
|
10
|
+
let(:date) { "2017-01-01" }
|
11
|
+
|
12
|
+
it { line_added "- #{date}: #{description}" }
|
13
|
+
it { stdout_only "Activity added: \"#{date}: #{description}\"" } if test_stdout
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "when date is in MM-DD-YYYY" do
|
17
|
+
let(:date) { "01-02-2017" }
|
18
|
+
|
19
|
+
it { line_added "- 2017-01-02: #{description}" }
|
20
|
+
it { stdout_only "Activity added: \"2017-01-02: #{description}\"" } if test_stdout
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "when date is invalid" do
|
24
|
+
let(:date) { "2017-02-30" }
|
25
|
+
|
26
|
+
it { line_added "- 2017-03-02: #{description}" }
|
27
|
+
it { stdout_only "Activity added: \"2017-03-02: #{description}\"" } if test_stdout
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "when date is natural language" do
|
31
|
+
let(:date) { "February 23rd, 2017" }
|
32
|
+
|
33
|
+
it { line_added "- 2017-02-23: #{description}" }
|
34
|
+
it { stdout_only "Activity added: \"2017-02-23: #{description}\"" } if test_stdout
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def description_parsing_specs(test_stdout: true)
|
40
|
+
describe "description parsing" do
|
41
|
+
let(:date) { Date.today.strftime }
|
42
|
+
|
43
|
+
describe "when description includes a friend's full name (case insensitive)" do
|
44
|
+
let(:description) { "Lunch with grace hopper." }
|
45
|
+
|
46
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**." }
|
47
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper.\"" } if test_stdout
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "when description includes a friend's first name (case insensitive)" do
|
51
|
+
let(:description) { "Lunch with grace." }
|
52
|
+
|
53
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**." }
|
54
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper.\"" } if test_stdout
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "when description has a friend's first name and last initial (case insensitive)" do
|
58
|
+
describe "when followed by no period" do
|
59
|
+
let(:description) { "Lunch with grace h" }
|
60
|
+
|
61
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**" }
|
62
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper\"" } if test_stdout
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "when followed by a period at the end of a sentence" do
|
66
|
+
let(:description) { "Met grace h. So fun!" }
|
67
|
+
|
68
|
+
it { line_added "- #{date}: Met **Grace Hopper**. So fun!" }
|
69
|
+
it { stdout_only "Activity added: \"#{date}: Met Grace Hopper. So fun!\"" } if test_stdout
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "when followed by a period at the end of the description" do
|
73
|
+
let(:description) { "Lunch with grace h." }
|
74
|
+
|
75
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**." }
|
76
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper.\"" } if test_stdout
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "when followed by a period in the middle of a sentence" do
|
80
|
+
let(:description) { "Met grace h. at 12." }
|
81
|
+
|
82
|
+
it { line_added "- #{date}: Met **Grace Hopper** at 12." }
|
83
|
+
it { stdout_only "Activity added: \"#{date}: Met Grace Hopper at 12.\"" } if test_stdout
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "when description includes a friend's nickname (case insensitive)" do
|
88
|
+
let(:description) { "Lunch with the admiral." }
|
89
|
+
|
90
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**." }
|
91
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper.\"" } if test_stdout
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "when description includes a friend's nickname which contains a name" do
|
95
|
+
let(:description) { "Lunch with Amazing Grace." }
|
96
|
+
|
97
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper**." }
|
98
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace Hopper.\"" } if test_stdout
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "when description includes a friend's name at the beginning of a word" do
|
102
|
+
# Capitalization reduces chance of a false positive.
|
103
|
+
let(:description) { "Gracefully strolled." }
|
104
|
+
|
105
|
+
it { line_added "- #{date}: Gracefully strolled." }
|
106
|
+
it { stdout_only "Activity added: \"#{date}: Gracefully strolled.\"" } if test_stdout
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "when description includes a friend's name at the end of a word" do
|
110
|
+
# Capitalization reduces chance of a false positive.
|
111
|
+
let(:description) { "The service was a disGrace." }
|
112
|
+
|
113
|
+
it { line_added "- #{date}: The service was a disGrace." }
|
114
|
+
it { stdout_only "Activity added: \"#{date}: The service was a disGrace.\"" } if test_stdout
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "when description includes a friend's name in the middle of a word" do
|
118
|
+
# Capitalization reduces chance of a false positive.
|
119
|
+
let(:description) { "The service was disGraceful." }
|
120
|
+
|
121
|
+
it { line_added "- #{date}: The service was disGraceful." }
|
122
|
+
it { stdout_only "Activity added: \"#{date}: The service was disGraceful.\"" } if test_stdout
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "when a friend's name is escaped with a backslash" do
|
126
|
+
# We have to use four backslashes here because of Ruby's backslash escaping; when this
|
127
|
+
# goes through all of the layers of this test it emerges on the other side as a single one.
|
128
|
+
let(:description) { "Dinner with \\\\Grace Kelly." }
|
129
|
+
|
130
|
+
it { line_added "- #{date}: Dinner with Grace Kelly." }
|
131
|
+
it { stdout_only "Activity added: \"#{date}: Dinner with Grace Kelly.\"" } if test_stdout
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "hyphenated name edge cases" do
|
135
|
+
describe "when one name precedes another before a hyphen" do
|
136
|
+
let(:description) { "Shopped w/ Mary-Kate." }
|
137
|
+
|
138
|
+
# Make sure "Mary" is a closer friend than "Mary-Kate" so we know our
|
139
|
+
# test result isn't due to chance.
|
140
|
+
let(:content) do
|
141
|
+
<<-FILE
|
142
|
+
### Activities:
|
143
|
+
- 2017-01-01: Singing with **Mary Poppins**.
|
144
|
+
|
145
|
+
### Friends:
|
146
|
+
- Mary Poppins
|
147
|
+
- Mary-Kate Olsen
|
148
|
+
|
149
|
+
### Locations:
|
150
|
+
FILE
|
151
|
+
end
|
152
|
+
|
153
|
+
it { line_added "- #{date}: Shopped w/ **Mary-Kate Olsen**." }
|
154
|
+
it { stdout_only "Activity added: \"#{date}: Shopped w/ Mary-Kate Olsen.\"" } if test_stdout
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "when one name follows another after a hyphen" do
|
158
|
+
let(:description) { "Shopped w/ Mary-Kate." }
|
159
|
+
|
160
|
+
# Make sure "Kate" is a closer friend than "Mary-Kate" so we know our
|
161
|
+
# test result isn't due to chance.
|
162
|
+
let(:content) do
|
163
|
+
<<-FILE
|
164
|
+
### Activities:
|
165
|
+
- 2017-01-01: Improv with **Kate Winslet**.
|
166
|
+
|
167
|
+
### Friends:
|
168
|
+
- Kate Winslet
|
169
|
+
- Mary-Kate Olsen
|
170
|
+
|
171
|
+
### Locations:
|
172
|
+
FILE
|
173
|
+
end
|
174
|
+
|
175
|
+
it { line_added "- #{date}: Shopped w/ **Mary-Kate Olsen**." }
|
176
|
+
it { stdout_only "Activity added: \"#{date}: Shopped w/ Mary-Kate Olsen.\"" } if test_stdout
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "when one name is contained within another via hyphens" do
|
180
|
+
let(:description) { "Met Mary-Jo-Kate." }
|
181
|
+
|
182
|
+
# Make sure "Jo" is a closer friend than "Mary-Jo-Kate" so we know our
|
183
|
+
# test result isn't due to chance.
|
184
|
+
let(:content) do
|
185
|
+
<<-FILE
|
186
|
+
### Activities:
|
187
|
+
- 2017-01-01: Singing with **Jo Stafford**.
|
188
|
+
|
189
|
+
### Friends:
|
190
|
+
- Jo Stafford
|
191
|
+
- Mary-Jo-Kate Olsen
|
192
|
+
|
193
|
+
### Locations:
|
194
|
+
FILE
|
195
|
+
end
|
196
|
+
|
197
|
+
it { line_added "- #{date}: Met **Mary-Jo-Kate Olsen**." }
|
198
|
+
it { stdout_only "Activity added: \"#{date}: Met Mary-Jo-Kate Olsen.\"" } if test_stdout
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "when description has a friend's name with leading asterisks" do
|
203
|
+
let(:description) { "Lunch with **Grace Hopper." }
|
204
|
+
|
205
|
+
it { line_added "- #{date}: Lunch with **Grace Hopper." }
|
206
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with **Grace Hopper.\"" } if test_stdout
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "when description has a friend's name with trailing asterisks" do
|
210
|
+
# Note: We can't guarantee that "Grace Hopper**" doesn't match because the "Grace" isn't
|
211
|
+
# surrounded by asterisks.
|
212
|
+
let(:description) { "Lunch with Grace**." }
|
213
|
+
|
214
|
+
it { line_added "- #{date}: Lunch with Grace**." }
|
215
|
+
it { stdout_only "Activity added: \"#{date}: Lunch with Grace**.\"" } if test_stdout
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "when description has a friend's name multiple times" do
|
219
|
+
let(:description) { "Grace! Grace!!!" }
|
220
|
+
|
221
|
+
it { line_added "- #{date}: **Grace Hopper**! **Grace Hopper**!!!" }
|
222
|
+
it { stdout_only "Activity added: \"#{date}: Grace Hopper! Grace Hopper!!!\"" } if test_stdout
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "when description has a name with multiple friend matches" do
|
226
|
+
describe "when there is useful context from past activities" do
|
227
|
+
let(:description) { "Met John + Elizabeth." }
|
228
|
+
|
229
|
+
# Create a past activity in which Elizabeth Cady Stanton did something
|
230
|
+
# with John Cage. Then, create past activities to make Elizabeth II a
|
231
|
+
# better friend than Elizabeth Cady Stanton.
|
232
|
+
let(:content) do
|
233
|
+
<<-FILE
|
234
|
+
### Activities:
|
235
|
+
- 2017-01-05: Picnic with **Elizabeth Cady Stanton** and **John Cage**.
|
236
|
+
- 2017-01-04: Got lunch with **Elizabeth II**.
|
237
|
+
- 2017-01-03: Ice skated with **Elizabeth II**.
|
238
|
+
|
239
|
+
### Friends:
|
240
|
+
- Elizabeth Cady Stanton
|
241
|
+
- Elizabeth II
|
242
|
+
- John Cage
|
243
|
+
|
244
|
+
### Locations:
|
245
|
+
FILE
|
246
|
+
end
|
247
|
+
|
248
|
+
# Elizabeth II is the better friend, but historical activities have
|
249
|
+
# had Elizabeth Cady Stanton and John Cage together. Thus, we should
|
250
|
+
# interpret "Elizabeth" as Elizabeth Cady Stanton.
|
251
|
+
it { line_added "- #{date}: Met **John Cage** + **Elizabeth Cady Stanton**." }
|
252
|
+
if test_stdout
|
253
|
+
it { stdout_only "Activity added: \"#{date}: Met John Cage + Elizabeth Cady Stanton.\"" }
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "when there is no useful context from past activities" do
|
258
|
+
let(:description) { "Dinner with John and Elizabeth." }
|
259
|
+
|
260
|
+
# Create a past activity in which Elizabeth Cady Stanton did something
|
261
|
+
# with John Cage. Then, create past activities to make Elizabeth II a
|
262
|
+
# better friend than Elizabeth Cady Stanton.
|
263
|
+
let(:content) do
|
264
|
+
<<-FILE
|
265
|
+
### Activities:
|
266
|
+
- 2017-01-03: Ice skated with **Elizabeth II**.
|
267
|
+
|
268
|
+
### Friends:
|
269
|
+
- Elizabeth Cady Stanton
|
270
|
+
- Elizabeth II
|
271
|
+
- John Cage
|
272
|
+
|
273
|
+
### Locations:
|
274
|
+
FILE
|
275
|
+
end
|
276
|
+
|
277
|
+
# Pick the "Elizabeth" with more activities.
|
278
|
+
it { line_added "- #{date}: Dinner with **John Cage** and **Elizabeth II**." }
|
279
|
+
if test_stdout
|
280
|
+
it { stdout_only "Activity added: \"#{date}: Dinner with John Cage and Elizabeth II.\"" }
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "when description contains a location name (case insensitive)" do
|
286
|
+
let(:description) { "Lunch at a cafe in paris." }
|
287
|
+
|
288
|
+
it { line_added "- #{date}: Lunch at a cafe in _Paris_." }
|
289
|
+
it { stdout_only "Activity added: \"#{date}: Lunch at a cafe in Paris.\"" } if test_stdout
|
290
|
+
end
|
291
|
+
|
292
|
+
describe "when description contains both names and locations" do
|
293
|
+
let(:description) { "Grace and I went to Atlantis and then Paris for lunch with George." }
|
294
|
+
|
295
|
+
it do
|
296
|
+
line_added "- #{date}: **Grace Hopper** and I went to _Atlantis_ and then _Paris_ for "\
|
297
|
+
"lunch with **George Washington Carver**."
|
298
|
+
end
|
299
|
+
if test_stdout
|
300
|
+
it do
|
301
|
+
stdout_only "Activity added: \"#{date}: Grace Hopper and I went to Atlantis and then "\
|
302
|
+
"Paris for lunch with George Washington Carver.\""
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
clean_describe "add activity" do
|
310
|
+
let(:content) { CONTENT }
|
311
|
+
|
312
|
+
describe "date ordering" do
|
313
|
+
let(:content) do
|
314
|
+
<<-FILE
|
315
|
+
### Activities:
|
316
|
+
- 2017-01-01: Activity 1.
|
317
|
+
|
318
|
+
### Friends:
|
319
|
+
|
320
|
+
### Locations:
|
321
|
+
FILE
|
322
|
+
end
|
323
|
+
|
324
|
+
subject do
|
325
|
+
run_cmd("add activity 2017-01-01: Activity 2.")
|
326
|
+
run_cmd("add activity 2017-01-01: Activity 3.")
|
327
|
+
run_cmd("add activity 2017-01-01: Activity 4.")
|
328
|
+
end
|
329
|
+
|
330
|
+
it "orders dates by insertion time" do
|
331
|
+
subject
|
332
|
+
File.read(filename).must_equal <<-FILE
|
333
|
+
### Activities:
|
334
|
+
- 2017-01-01: Activity 4.
|
335
|
+
- 2017-01-01: Activity 3.
|
336
|
+
- 2017-01-01: Activity 2.
|
337
|
+
- 2017-01-01: Activity 1.
|
338
|
+
|
339
|
+
### Friends:
|
340
|
+
|
341
|
+
### Locations:
|
342
|
+
FILE
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
describe "when given a date and a description in the command" do
|
347
|
+
subject { run_cmd("add activity #{date}: #{description}") }
|
348
|
+
|
349
|
+
date_parsing_specs
|
350
|
+
description_parsing_specs
|
351
|
+
end
|
352
|
+
|
353
|
+
describe "when given only a date in the command" do
|
354
|
+
subject { run_cmd("add activity #{date}", stdin_data: description) }
|
355
|
+
|
356
|
+
# We don't try to test the STDOUT here because our command prompt produces other STDOUT that's
|
357
|
+
# hard to test.
|
358
|
+
date_parsing_specs(test_stdout: false)
|
359
|
+
description_parsing_specs(test_stdout: false)
|
360
|
+
end
|
361
|
+
|
362
|
+
describe "when given only a description in the command" do
|
363
|
+
subject { run_cmd("add activity #{description}") }
|
364
|
+
|
365
|
+
# We don't test date parsing since in this case the date is always inferred to be today.
|
366
|
+
|
367
|
+
description_parsing_specs
|
368
|
+
end
|
369
|
+
|
370
|
+
describe "when given neither a date nor a description in the command" do
|
371
|
+
subject { run_cmd("add activity", stdin_data: description) }
|
372
|
+
|
373
|
+
# We don't test date parsing since in this case the date is always inferred to be today.
|
374
|
+
|
375
|
+
# We don't try to test the STDOUT here because our command prompt produces other STDOUT that's
|
376
|
+
# hard to test.
|
377
|
+
description_parsing_specs(test_stdout: false)
|
378
|
+
end
|
379
|
+
end
|