graffle 0.1.9 → 0.2.0

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.
Files changed (42) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +15 -8
  3. data/Rakefile.hoe +1 -2
  4. data/bin/interpret-intentions.rb +51 -0
  5. data/examples/objects with notes.rb +1 -0
  6. data/examples/old-style-rails-workflow-test.expected +11 -0
  7. data/examples/old-style-rails-workflow-test.rb +43 -0
  8. data/examples/rails-workflow-test.rb +7 -6
  9. data/examples/sheet with key.expected +5 -0
  10. data/examples/sheet with key.graffle +0 -0
  11. data/examples/sheet with key.rb +38 -0
  12. data/graffle.tmproj +242 -75
  13. data/lib/graffle.rb +11 -2
  14. data/lib/graffle/nodoc/hacks.rb +1 -0
  15. data/lib/graffle/point.rb +1 -0
  16. data/lib/graffle/stereotypes.rb +34 -3
  17. data/lib/graffle/version.rb +3 -1
  18. data/lib/graphical_tests_for_rails.rb +11 -9
  19. data/lib/graphical_tests_for_rails/jumbled-string-canonicalizer.rb +117 -0
  20. data/lib/graphical_tests_for_rails/orderings.rb +1 -2
  21. data/lib/graphical_tests_for_rails/picture-applier.rb +257 -0
  22. data/lib/graphical_tests_for_rails/stereotype-extensions.rb +111 -0
  23. data/lib/graphical_tests_for_rails/text-appliers.rb +10 -84
  24. data/lib/graphical_tests_for_rails/user-intention.rb +250 -0
  25. data/test/abstract-graphic-tests.rb +10 -0
  26. data/test/container-tests.rb +29 -0
  27. data/test/graphical_tests_for_rails/jumbled-string-canonicalizer-tests.rb +174 -0
  28. data/test/graphical_tests_for_rails/new-style-picture-applier-tests.rb +286 -0
  29. data/test/graphical_tests_for_rails/old-style-picture-applier-tests.rb +129 -0
  30. data/test/graphical_tests_for_rails/user-intention-tests.rb +389 -0
  31. data/test/graphical_tests_for_rails/util.rb +9 -0
  32. data/test/sheet-tests.rb +21 -0
  33. data/test/text-tests.rb +6 -0
  34. metadata +25 -26
  35. data/design-notes/graphical-tests-for-rails-objects.graffle +0 -644
  36. data/lib/graphical_tests_for_rails/graphic-volunteers.rb +0 -75
  37. data/lib/graphical_tests_for_rails/picture-appliers.rb +0 -225
  38. data/lib/graphical_tests_for_rails/volunteer-pool.rb +0 -115
  39. data/test/graphical_tests_for_rails/deprecated-graphic-interpreter-tests.rb +0 -121
  40. data/test/graphical_tests_for_rails/graphic-volunteer-tests.rb +0 -218
  41. data/test/graphical_tests_for_rails/picture-applier-tests.rb +0 -215
  42. data/test/graphical_tests_for_rails/text-applier-tests.rb +0 -111
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-20.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ include S4tUtils
9
+
10
+ require "../set-standalone-test-paths.rb" unless $started_from_rakefile
11
+
12
+ require 'extensions/string'
13
+ require 'test/graphical_tests_for_rails/util'
14
+ require 'graffle'
15
+ require 'graphical_tests_for_rails/picture-applier'
16
+
17
+ class PictureApplierTest < Test::Unit::TestCase
18
+ include Graffle
19
+ include Graffle::Builders
20
+ include GraphicalTestsForRails
21
+ include GraphicalTestsForRails::TextAppliers
22
+
23
+ def setup
24
+ @sheet = sheet {
25
+ with shaped_graphic {
26
+ graffle_id_is 1
27
+ with_content 'shaped graphic text...'
28
+ }
29
+ with line_label {
30
+ for_line 22
31
+ with_content 'line label text...'
32
+ }
33
+ with line_graphic {
34
+ graffle_id_is 22
35
+ }
36
+ with shaped_graphic { # no text
37
+ graffle_id_is 333
38
+ }
39
+ with line_graphic { # no label
40
+ graffle_id_is 4444
41
+ }
42
+ }
43
+ end
44
+
45
+ def shaped_graphic_with_content; @sheet.find_by_id(1); end
46
+ def shaped_graphic_without_text; @sheet.find_by_id(333); end
47
+ def line_graphic_with_label; @sheet.find_by_id(22); end
48
+ def line_graphic_without_label; @sheet.find_by_id(4444); end
49
+
50
+
51
+ def test_there_is_a_fixed_set_of_graphically_oriented_volunteers
52
+ assert_raises(StandardError) {
53
+ PictureApplier.new {
54
+ given('oopsla').skip
55
+ }
56
+ }
57
+ end
58
+
59
+
60
+ # An example of how it's all put together to send messages to targets.
61
+
62
+
63
+ def test_application_of_matches
64
+ @applier = PictureApplier.new {
65
+ given('note').matching('text...').skip
66
+ given('note').use(ArgsFromQuotedText.new)
67
+ }
68
+
69
+ s = sheet {
70
+ with shaped_graphic { with_notes 'hello world' }
71
+ with line_graphic
72
+ with shaped_graphic { with_notes "waiting..."}
73
+ with line_graphic { with_notes "goodbye world" }
74
+ }
75
+
76
+ target = CommandRecorder.new
77
+ @applier.apply(s.graphics, target)
78
+ assert_equal(['hello_world()', 'goodbye_world()'],
79
+ target.record)
80
+ end
81
+
82
+
83
+ class ThriceThenDie
84
+ include Test::Unit::Assertions
85
+
86
+ def initialize
87
+ @count = 0
88
+ end
89
+
90
+ def assert_on_page(value)
91
+ @count += 1
92
+ assert_equal("right", "wrong") if @count >= 4
93
+ end
94
+
95
+ end
96
+
97
+ def four_counting_shaped_graphics
98
+ sheet {
99
+ with shaped_graphic { with_content "first" }
100
+ with shaped_graphic { with_content "second" }
101
+ with shaped_graphic { with_content "third" }
102
+ with shaped_graphic { with_content "yikes!"}
103
+ }.graphics
104
+ end
105
+
106
+ def four_graphic_contents
107
+ four_counting_shaped_graphics.collect { |g| g.content.as_plain_text }
108
+ end
109
+
110
+ def assert_thrice_then_die_log(exc, expected_echoed)
111
+ assert_match(/right.*expected.*was.*wrong/m, exc.message)
112
+ expected = expected_echoed.collect do |w|
113
+ "assert_on_page\\(" + w.inspect + "\\)"
114
+ end.join('.*')
115
+ assert_match(/#{expected}/m, exc.message)
116
+ end
117
+
118
+ def test_logging_prints_list_of_calls_into_target
119
+ @applier = PictureApplier.new {
120
+ given('shaped graphic content').use(TextAsNameOfPage.new('assert_on_page'))
121
+ }
122
+ begin
123
+ @applier.apply(four_counting_shaped_graphics, ThriceThenDie.new)
124
+ rescue Test::Unit::AssertionFailedError => e
125
+ assert_thrice_then_die_log(e, four_graphic_contents)
126
+ end
127
+ end
128
+
129
+ end
@@ -0,0 +1,389 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-31.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ include S4tUtils
9
+
10
+ require "../set-standalone-test-paths.rb" unless $started_from_rakefile
11
+
12
+ require 'graffle'
13
+ require 'graphical_tests_for_rails/user-intention'
14
+ require 'test/graphical_tests_for_rails/util'
15
+
16
+
17
+ class TestUserIntention < Test::Unit::TestCase
18
+ include GraphicalTestsForRails
19
+ include TextAppliers
20
+ include Graffle::Builders
21
+ include UserIntention::Names
22
+
23
+ def self.make_assert_foo_understood(foo)
24
+ class_eval %Q{def assert_#{foo}_understood(expected, input)
25
+ terp = UserIntention.new
26
+ terp.understand_#{foo}(input)
27
+ assert_equal(expected, terp.#{foo}.value)
28
+ end}
29
+ end
30
+ make_assert_foo_understood('place')
31
+ make_assert_foo_understood('text')
32
+ make_assert_foo_understood('action')
33
+ make_assert_foo_understood('line_guard')
34
+
35
+ def setup
36
+ @note = UserIntention.understand("ignore note")
37
+ @content = UserIntention.understand("ignore content")
38
+ @line_note = UserIntention.understand("ignore line note")
39
+ @line_label_note = UserIntention.understand("ignore line label note")
40
+ @shaped_graphic_note = UserIntention.understand("ignore shape note")
41
+ @line_label_content = UserIntention.understand("ignore line label content")
42
+ @shaped_graphic_content = UserIntention.understand("ignore shape content")
43
+
44
+
45
+ end
46
+
47
+ def test_user_intention_can_identify_annotation_forms
48
+ assert_place_understood(USER_MEANS_ANY_NOTE, "notes blah")
49
+ assert_place_understood(USER_MEANS_ANY_NOTE, "blah notes blah")
50
+ assert_place_understood(USER_MEANS_ANY_NOTE, "blah note")
51
+ assert_place_understood(USER_MEANS_ANY_NOTE, "blah annotation")
52
+ assert_place_understood(USER_MEANS_ANY_NOTE, "annotations")
53
+ end
54
+
55
+ def test_user_intention_can_identify_content_forms
56
+ assert_place_understood(USER_MEANS_ANY_CONTENT, "contents blah")
57
+ assert_place_understood(USER_MEANS_ANY_CONTENT, "blah contents blah")
58
+ assert_place_understood(USER_MEANS_ANY_CONTENT, "blah content")
59
+ end
60
+
61
+ def test_user_intention_can_understand_combined_forms
62
+ assert_place_understood(USER_MEANS_LINE_LABEL_CONTENT, "line label's content")
63
+ assert_place_understood(USER_MEANS_LINE_LABEL_NOTE, "line label note")
64
+ assert_place_understood(USER_MEANS_LINE_NOTE, "line with annotation")
65
+ assert_place_understood(USER_MEANS_SHAPED_GRAPHIC_CONTENT, "shaped graphic content")
66
+ assert_place_understood(USER_MEANS_SHAPED_GRAPHIC_CONTENT, "content of shape")
67
+ assert_place_understood(USER_MEANS_SHAPED_GRAPHIC_NOTE, "note on shape")
68
+ end
69
+
70
+ def test_user_intention_requires_some_sort_of_graphic
71
+ assert_raises_with_matching_message(StandardError, /blah'.*does not.*refer to any graphic/) {
72
+ terp = UserIntention.new
73
+ terp.understand_place("blah blah blah")
74
+ }
75
+ end
76
+
77
+
78
+ def test_user_intention_can_understand_controlling_text
79
+ assert_text_understood(USER_MEANS_CONTAINS_IGNORE, "shape ignore content")
80
+ end
81
+
82
+ def test_it_is_ok_for_the_input_to_say_nothing_about_text_matching
83
+ assert_text_understood(nil, 'note')
84
+ end
85
+
86
+
87
+
88
+ def test_user_intention_can_understand_actions
89
+ assert_action_understood(USER_MEANS_DO_NOTHING, "skip notes matching ignore")
90
+ assert_action_understood(USER_MEANS_ACTOR_ACTION_WITH_QUOTE_ARGS,
91
+ "line labels are user actions with quoted args")
92
+ assert_action_understood(USER_MEANS_ACTOR_ACTION_WITH_QUOTE_ARGS,
93
+ "a note is a user action with arguments in quotes")
94
+ assert_action_understood(USER_MEANS_CLAIM_WITH_QUOTE_ARGS,
95
+ "contents are claims with arguments in quotes")
96
+ assert_action_understood(USER_MEANS_PAGE_NAME,
97
+ "contents name a page")
98
+ end
99
+
100
+ def test_there_must_be_an_action_in_a_line
101
+ assert_raises_with_matching_message(StandardError, /blah'.*doesn't.*say what should happen/) {
102
+ terp = UserIntention.new
103
+ terp.understand_action("blah blah blah")
104
+ }
105
+ end
106
+
107
+ def test_there_must_be_only_one_action_in_a_line
108
+ assert_raises_with_matching_message(StandardError, /conflicting instructions/) {
109
+ terp = UserIntention.new
110
+ terp.understand_action("notes that skip page and name a page")
111
+ }
112
+ end
113
+
114
+ def test_discovery_of_line_guards
115
+ assert_line_guard_understood(USER_MEANS_LINE_ENDS_WITH_ELLIPSES,
116
+ "line with ellipses")
117
+ assert_line_guard_understood(USER_MEANS_LINE_ENDS_WITH_ELLIPSES,
118
+ "line containing text...")
119
+ assert_line_guard_understood(USER_MEANS_LINES_WITH_ONE_WORD,
120
+ "a shape content line with one word names a page")
121
+ assert_line_guard_understood(USER_MEANS_LINES_WITH_ONE_WORD,
122
+ "a shape content line with only one word names a page")
123
+ assert_line_guard_understood(USER_MEANS_REMAINING_LINES,
124
+ "otherwise, a shape content line names a page" )
125
+ assert_line_guard_understood(USER_MEANS_REMAINING_LINES,
126
+ "other kinds of shape content lines name a page" )
127
+ end
128
+
129
+ def test_line_guards_may_be_omitted
130
+ assert_line_guard_understood(nil, "note")
131
+ end
132
+
133
+
134
+
135
+ def test_intention_does_not_apply_to_naked_graphic
136
+ g = shaped_graphic
137
+ assert_false(@note.applies_to_graphic?(g))
138
+ assert_false(@content.applies_to_graphic?(g))
139
+ assert_false(@line_note.applies_to_graphic?(g))
140
+ assert_false(@line_label_note.applies_to_graphic?(g))
141
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
142
+ assert_false(@line_label_content.applies_to_graphic?(g))
143
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
144
+ end
145
+
146
+ LINES = "hello! \n\n there!" # Note whitespace
147
+ SPLIT_LINES = ["hello!", "there!"]
148
+
149
+ def test_intention_can_apply_to_shaped_graphic_with_content
150
+ g = shaped_graphic { with_content LINES }
151
+ assert_false(@note.applies_to_graphic?(g))
152
+ assert_true(@content.applies_to_graphic?(g))
153
+ assert_false(@line_note.applies_to_graphic?(g))
154
+ assert_false(@line_label_note.applies_to_graphic?(g))
155
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
156
+ assert_false(@line_label_content.applies_to_graphic?(g))
157
+ assert_true(@shaped_graphic_content.applies_to_graphic?(g))
158
+
159
+ assert_equal(SPLIT_LINES, @content.lines_from(g))
160
+ assert_equal(SPLIT_LINES, @shaped_graphic_content.lines_from(g))
161
+ end
162
+
163
+
164
+
165
+ def test_intention_can_apply_to_shaped_graphic_with_note
166
+ g = shaped_graphic { with_note LINES }
167
+ assert_true(@note.applies_to_graphic?(g))
168
+ assert_false(@content.applies_to_graphic?(g))
169
+ assert_false(@line_note.applies_to_graphic?(g))
170
+ assert_false(@line_label_note.applies_to_graphic?(g))
171
+ assert_true(@shaped_graphic_note.applies_to_graphic?(g))
172
+ assert_false(@line_label_content.applies_to_graphic?(g))
173
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
174
+
175
+ assert_equal(SPLIT_LINES, @note.lines_from(g))
176
+ assert_equal(SPLIT_LINES, @shaped_graphic_note.lines_from(g))
177
+ end
178
+
179
+ def test_intention_does_not_apply_to_naked_line
180
+
181
+ g = besheeted(line_graphic)
182
+ assert_false(@note.applies_to_graphic?(g))
183
+ assert_false(@content.applies_to_graphic?(g))
184
+ assert_false(@line_note.applies_to_graphic?(g))
185
+ assert_false(@line_label_note.applies_to_graphic?(g))
186
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
187
+ assert_false(@line_label_content.applies_to_graphic?(g))
188
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
189
+ end
190
+
191
+ def test_intention_can_apply_to_line_graphic_with_note
192
+ g = besheeted(line_graphic { with_note LINES})
193
+ assert_true(@note.applies_to_graphic?(g))
194
+ assert_false(@content.applies_to_graphic?(g))
195
+ assert_true(@line_note.applies_to_graphic?(g))
196
+ assert_false(@line_label_note.applies_to_graphic?(g))
197
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
198
+ assert_false(@line_label_content.applies_to_graphic?(g))
199
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
200
+
201
+ assert_equal(SPLIT_LINES, @note.lines_from(g))
202
+ assert_equal(SPLIT_LINES, @line_note.lines_from(g))
203
+ end
204
+
205
+ def test_intention_can_apply_to_line_label_with_content
206
+
207
+ g = besheeted(line_graphic { graffle_id_is 1},
208
+ line_label {
209
+ for_line 1
210
+ with_content LINES
211
+ })
212
+ assert_false(@note.applies_to_graphic?(g))
213
+ assert_true(@content.applies_to_graphic?(g))
214
+ assert_false(@line_note.applies_to_graphic?(g))
215
+ assert_false(@line_label_note.applies_to_graphic?(g))
216
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
217
+ assert_true(@line_label_content.applies_to_graphic?(g))
218
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
219
+
220
+ assert_equal(SPLIT_LINES, @content.lines_from(g))
221
+ assert_equal(SPLIT_LINES, @line_label_content.lines_from(g))
222
+ end
223
+
224
+ def test_intention_can_apply_to_line_label_with_note
225
+ g = besheeted(line_graphic { graffle_id_is 1},
226
+ line_label {
227
+ for_line 1
228
+ with_note LINES
229
+ })
230
+ assert_true(@note.applies_to_graphic?(g))
231
+ assert_false(@content.applies_to_graphic?(g))
232
+ assert_false(@line_note.applies_to_graphic?(g))
233
+ assert_true(@line_label_note.applies_to_graphic?(g))
234
+ assert_false(@shaped_graphic_note.applies_to_graphic?(g))
235
+ assert_false(@line_label_content.applies_to_graphic?(g))
236
+ assert_false(@shaped_graphic_content.applies_to_graphic?(g))
237
+
238
+ assert_equal(SPLIT_LINES, @note.lines_from(g))
239
+ assert_equal(SPLIT_LINES, @line_label_note.lines_from(g))
240
+ end
241
+
242
+ def test_intentions_applies_to_text_by_default
243
+ intention = UserIntention.understand("note is page name")
244
+ assert_true(intention.applies_to_text?(["some text"]))
245
+ end
246
+
247
+ def test_intentions_can_be_told_to_match_ignore
248
+ intention = UserIntention.understand("note with 'ignore'")
249
+ assert_true(intention.applies_to_text?(["ignore"]))
250
+ assert_true(intention.applies_to_text?(["line ending with ignore "]))
251
+ assert_true(intention.applies_to_text?(["lines, ending, with, ignore"]))
252
+
253
+ assert_false(intention.applies_to_text?(["some text"]))
254
+ assert_false(intention.applies_to_text?(["ignore begins line"]))
255
+ end
256
+
257
+ def test_individual_lines_are_processed_by_default
258
+ intention = UserIntention.understand("note is page name")
259
+ assert_true(intention.applies_to_line?(["some text"]))
260
+ end
261
+
262
+ def test_individual_lines_with_more_than_one_word_can_be_skipped
263
+ intention = UserIntention.understand("notes with one word are page names")
264
+ assert_false(intention.applies_to_line?("some text"))
265
+ assert_true(intention.applies_to_line?("text"))
266
+ end
267
+
268
+ def test_individual_lines_can_be_governed_by_ellipses
269
+ intention = UserIntention.understand("note text... is page name")
270
+ assert_true(intention.applies_to_line?("some text..."))
271
+ assert_false(intention.applies_to_line?("ellipses... must end line"))
272
+ end
273
+
274
+ def test_individual_lines_can_be_handled_with_a_default_clause
275
+ intention = UserIntention.understand("skip notes with any other kind of line")
276
+ assert_true(intention.applies_to_line?("some text"))
277
+ assert_true(intention.applies_to_line?("text"))
278
+ end
279
+
280
+ end
281
+
282
+ class TestUserIntentionActionsApplied < Test::Unit::TestCase
283
+ include GraphicalTestsForRails
284
+
285
+ def setup
286
+ @target = CommandRecorder.new
287
+ end
288
+
289
+ def assert_last_translation(expected, input)
290
+ @intention.apply(input, @target)
291
+ assert_equal(expected, @target.record.last)
292
+ end
293
+
294
+ def test_do_nothing_action
295
+ @intention = UserIntention.understand("skip notes")
296
+ @intention.apply("I am irrelevant", @target)
297
+ assert_equal([], @target.record)
298
+ end
299
+
300
+ def test_page_name_action
301
+ @intention = UserIntention.understand("notes name pages")
302
+ @intention.apply("MASTER", @target)
303
+ @intention.apply(" spaces-outside ", @target)
304
+ assert_equal(['assert_on_page("master")', 'assert_on_page("spaces-outside")'],
305
+ @target.record)
306
+ # TODO: don't know what makes sense for interior spaces or
307
+ # non-\w characters.
308
+ end
309
+
310
+ def test_claim_with_args_in_quotes_action
311
+ @intention = UserIntention.understand("notes are claims with args in quotes")
312
+
313
+ assert_last_translation('token()', "token")
314
+ assert_last_translation('underscore_separated_tokens()',
315
+ 'underscore separated tokens'
316
+ )
317
+ assert_last_translation('whitespace_is_handled_intelligently()',
318
+ " whitespace\tis handled intelligently "
319
+ )
320
+ assert_last_translation('here_is_an("argument")',
321
+ 'here is an "argument"'
322
+ )
323
+ assert_last_translation('single_quotes_are_args("too ")',
324
+ " single quotes are args 'too ' "
325
+ )
326
+ assert_last_translation('multiple("arguments", "work")',
327
+ %q{multiple 'arguments' "work"}
328
+ )
329
+ assert_last_translation('only_the("prefix", "command")',
330
+ %q{only the 'prefix' is included in the 'command'}
331
+ )
332
+
333
+ assert_last_translation('arguments_can("be", "comma", "separated")',
334
+ %q{arguments can 'be', 'comma', and 'separated'}
335
+ )
336
+
337
+ assert_last_translation('stuff_after_the("last", "argument")',
338
+ %q{stuff after the 'last' 'argument' is ignored.}
339
+ )
340
+
341
+ assert_last_translation('even_if_it_is_just("a period")',
342
+ %q{even if it is just 'a period'.}
343
+ )
344
+
345
+ assert_last_translation('the_method_ignores_punctuation()',
346
+ %q{the method (ignores) punctuation.}
347
+ )
348
+
349
+ assert_last_translation('except_that_dashes_become_underscore()',
350
+ %q{except that dashes become-underscore}
351
+ )
352
+
353
+ assert_last_translation("its_possible_to_have_quotes_within_words()",
354
+ %q{it's possible to have quotes within words}
355
+ )
356
+
357
+ assert_last_translation('while_a_messages_quotes_are_stripped("an argument\'s are retained")',
358
+ %q{while a message's quotes are stripped, "an argument's are retained"}
359
+ )
360
+
361
+ assert_last_translation("capitals_are_lowercased_throughout()",
362
+ %q{Capitals are lowercased THROUGHOUT})
363
+ end
364
+
365
+ def test_user_action_with_args_in_quotes_action
366
+ @intention = UserIntention.understand("notes are user actions with args in quotes")
367
+ assert_last_translation('runs("nathan")',
368
+ %q{Nathan runs})
369
+
370
+ assert_last_translation('runs_to("nathan", "Tacoma")',
371
+ %q{Nathan runs to "Tacoma"})
372
+
373
+ assert_raises_with_matching_message(StandardError, /'runs' cannot be split into a name and an action./) {
374
+ @intention.apply("runs", @target)
375
+ }
376
+ end
377
+
378
+ def test_user_action_logs
379
+ intention = UserIntention.understand("notes are user actions with args in quotes")
380
+ log = []
381
+ intention.apply('Nathan runs', @target, log)
382
+ assert_equal(['+ runs("nathan")'], log)
383
+
384
+ intention = UserIntention.understand("ignore notes")
385
+ intention.apply("Nathan runs", @target, log)
386
+ assert_equal(['+ runs("nathan")', "# 'Nathan runs' ignored."], log)
387
+
388
+ end
389
+ end