graffle 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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