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
@@ -101,4 +101,14 @@ class TestAbstractGraphics < Test::Unit::TestCase
101
101
  assert_true(s.find_by_id(333).note.behaves_like?(Graffle::Note))
102
102
  end
103
103
 
104
+ def test_abstract_graphics_in_collections_can_delete_themselves
105
+ s = sheet {
106
+ with shaped_graphic { graffle_id_is 1}
107
+ }
108
+
109
+ g = s.find_by_id(1)
110
+ g.delete_yourself
111
+ assert_equal(nil, s.find_by_id(1))
112
+ end
113
+
104
114
  end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-25.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # TODO: Some tests from sheets and groups could probably be moved here.
7
+
8
+
9
+ require "set-standalone-test-paths.rb" unless $started_from_rakefile
10
+ require 'test/unit'
11
+ require 's4t-utils'
12
+ include S4tUtils
13
+ require 'test/util'
14
+
15
+ require 'graffle'
16
+
17
+ class TestContainers < Test::Unit::TestCase
18
+ include Graffle
19
+ include Graffle::Builders
20
+
21
+ def test_deletion
22
+ s = sheet {
23
+ with line_graphic { graffle_id_is 1 }
24
+ }
25
+ g = s.find_by_id(1)
26
+ s.delete_graphic(g)
27
+ assert_nil(s.find_by_id(1))
28
+ end
29
+ end
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-29.
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 'graphical_tests_for_rails/jumbled-string-canonicalizer'
15
+
16
+ class TestJumbledStringCanonicalizer < Test::Unit::TestCase
17
+ include GraphicalTestsForRails
18
+
19
+ def test_simple_canonicalization_selects_one_string
20
+ patterns = {
21
+ [/\b(ones?)\b/] => "one",
22
+ }
23
+
24
+ izer = JumbledStringCanonicalizer.new(patterns)
25
+ canonical = izer.canonicalize(s = "they are the ones.")
26
+ assert_equal("one", canonical.value)
27
+ assert_equal(s, canonical.source)
28
+ assert_equal([['ones']], canonical.raw_reason)
29
+ assert_equal("'ones'", canonical.pretty_reason)
30
+ end
31
+
32
+ def test_canonicalization_can_have_more_than_one_regexp
33
+ patterns = {
34
+ [/\b(ones?\stwos?)\b/] => "wrong",
35
+ [/\b(ones?)\b/, /\b(twos?)\b/] => "one two",
36
+ }
37
+
38
+ izer = JumbledStringCanonicalizer.new(patterns)
39
+ canonical = izer.canonicalize(s = "two is two ones")
40
+ assert_equal("one two", canonical.value)
41
+ assert_equal(s, canonical.source)
42
+ assert_equal([['ones'], ['two']], canonical.raw_reason)
43
+ assert_equal("'ones'; and also 'two'", canonical.pretty_reason)
44
+ end
45
+
46
+ def test_raw_reasons_are_available
47
+ patterns = {
48
+ [/\b(a)\s+(cs?)\b/, /\b(d+).*?(e+).*?(f)\b/] => "success",
49
+ }
50
+
51
+ izer = JumbledStringCanonicalizer.new(patterns)
52
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
53
+ assert_equal("success", canonical.value)
54
+
55
+ assert_equal([["a", "cs"], ["ddd", "ee", "f"]], canonical.raw_reason)
56
+ end
57
+
58
+ def test_raw_reasons_do_not_include_nil_alternatives
59
+ patterns = {
60
+ [/(a)|(b)/] => "success",
61
+ }
62
+
63
+ izer = JumbledStringCanonicalizer.new(patterns)
64
+ canonical = izer.canonicalize(s = "a") # $1 = 'a', $2 = nil
65
+ assert_equal([["a"]], canonical.raw_reason)
66
+ end
67
+
68
+ def test_reasons_can_be_produced_prettily
69
+ patterns = {
70
+ [/\b(a)\s+(cs?)\b/, /\b(d+).*?(e+).*?(f)\b/] => "success",
71
+ }
72
+
73
+ izer = JumbledStringCanonicalizer.new(patterns)
74
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
75
+ assert_equal("success", canonical.value)
76
+ assert_equal("'a' and 'cs'; and also 'ddd', 'ee', and 'f'", canonical.pretty_reason)
77
+ end
78
+
79
+ def test_normally_no_match_is_an_error
80
+ izer = JumbledStringCanonicalizer.new
81
+ assert_raise_with_matching_message(StandardError,
82
+ /Cannot recognize.*'foo'/) {
83
+ izer.canonicalize("foo")
84
+ }
85
+ end
86
+
87
+ def test_the_no_match_error_message_can_be_customized
88
+ izer = JumbledStringCanonicalizer.new {
89
+ no_match_message {|source| "Dude! What's with '#{source}'?"}
90
+ }
91
+ assert_raise_with_matching_message(StandardError, /Dude! What's with 'foo'\?/) {
92
+ izer.canonicalize("foo")
93
+ }
94
+ end
95
+
96
+ def test_there_can_be_a_default_value_in_case_of_no_match
97
+ patterns = {
98
+ [/\b(ones?\stwos?)\b/] => "wrong",
99
+ [/\b(ones?)\b/, /\b(twos?)\b/] => "one two",
100
+ }
101
+ izer = JumbledStringCanonicalizer.new(patterns) {
102
+ default "default"
103
+ }
104
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
105
+ assert_equal("default", canonical.value)
106
+ assert_equal(s, canonical.source)
107
+ assert_equal([], canonical.raw_reason)
108
+ assert_equal("no matching words", canonical.pretty_reason)
109
+ end
110
+
111
+ def test_nil_is_a_fine_default_value
112
+ izer = JumbledStringCanonicalizer.new {
113
+ default nil
114
+ }
115
+ canonical = izer.canonicalize(s = "match me!")
116
+ assert_equal(nil, canonical.value)
117
+ assert_equal(s, canonical.source)
118
+ assert_equal([], canonical.raw_reason)
119
+ assert_equal("no matching words", canonical.pretty_reason)
120
+ end
121
+
122
+ def test_normally_multiple_matches_are_an_error
123
+ patterns = {
124
+ [/(one) (two)/] => 'one',
125
+ [/(two)/] => 'two'
126
+ }
127
+ izer = JumbledStringCanonicalizer.new(patterns)
128
+ assert_raise_with_matching_message(StandardError,
129
+ /Multiple matches in 'one two'.*'one' and 'two'/m) {
130
+ # Can't check both match reasons because order is unknown.
131
+ izer.canonicalize("one two")
132
+ }
133
+ end
134
+
135
+ def test_the_multiple_match_error_can_be_customized_using_pretty_reasons
136
+ patterns = {
137
+ [/(one) (two)/, /(one)/] => 'one',
138
+ [/(one) (two)/] => 'two'
139
+ }
140
+ izer = JumbledStringCanonicalizer.new(patterns) {
141
+ multiple_match_message { | source, pretty_reasons, raw_reasons |
142
+ pretty_reasons = pretty_reasons.sort_by { |r| r.length }
143
+ "Could not match '#{source}' for multiple reasons:\n" +
144
+ "One match is against these words: #{pretty_reasons[0]}\n" +
145
+ pretty_reasons[1..-1].collect { |r| "Another is against these: #{r}" }.join("\n")
146
+ }
147
+ }
148
+ assert_raise_with_matching_message(StandardError,
149
+ /not match 'one two'.*One match.*'one' and 'two'.*Another.*'one' and 'two'.*also 'one'/m) {
150
+ izer.canonicalize("one two")
151
+ }
152
+ end
153
+
154
+ def test_multiple_matches_can_be_disambiguated
155
+ patterns = {
156
+ [/(one) (two)/] => 'one',
157
+ [/(two)|(one)|(three)/] => 'two'
158
+ }
159
+ izer = JumbledStringCanonicalizer.new(patterns) {
160
+ disambiguate { |a, b|
161
+ words = proc { | x | - x.raw_reason.flatten.compact.length }
162
+ words[a] <=> words[b]
163
+ }
164
+ }
165
+ canonical = izer.canonicalize(s = "one two")
166
+ assert_equal("one", canonical.value)
167
+ assert_equal(s, canonical.source)
168
+ assert_equal("'one' and 'two'", canonical.pretty_reason)
169
+ end
170
+
171
+
172
+
173
+
174
+ end
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-25.
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 NewStylePictureApplierTest < Test::Unit::TestCase
18
+ include Graffle
19
+ include Graffle::Builders
20
+ include GraphicalTestsForRails
21
+
22
+ def setup
23
+ @sheet = sheet {
24
+ with shaped_graphic {
25
+ graffle_id_is 1
26
+ with_content "just a comment"
27
+ with_note 'ignore'
28
+ }
29
+
30
+ with shaped_graphic {
31
+ graffle_id_is 11
32
+ with_content "PAGE"
33
+ }
34
+
35
+ with line_label {
36
+ for_line 22
37
+ with_content "fred tests the label text"
38
+ }
39
+ with line_graphic {
40
+ graffle_id_is 22
41
+ }
42
+ with shaped_graphic {
43
+ with_content "Key"
44
+ with_note %Q{skip graphics with an "ignore" annotation
45
+
46
+ line label contents are user actions with args in quotes
47
+ shape content names a page
48
+ }
49
+ }
50
+
51
+ }
52
+ end
53
+
54
+
55
+ def test_from_sheet_requires_key
56
+ assert_raises_with_matching_message(StandardError, /has no key/) do
57
+ PictureApplier.from_sheet(sheet)
58
+ end
59
+ end
60
+
61
+
62
+ def test_but_defaults_can_be_used_instead_of_key
63
+ applier = PictureApplier.from_sheet(sheet,
64
+ "notes are claims with quoted args",
65
+ "contents are page names")
66
+
67
+ matches = lambda do |graphic|
68
+ ints = applier.intentions_that_apply_to(graphic)
69
+ ints.collect { |i| i.source }
70
+ end
71
+
72
+ benoted = shaped_graphic { with_note "fred" }
73
+ contented = shaped_graphic { with_content "barry" }
74
+
75
+ assert_equal(["notes are claims with quoted args"], matches[benoted])
76
+ assert_equal(["contents are page names"], matches[contented])
77
+ end
78
+
79
+
80
+ def test_from_sheet_key_requires_notes
81
+ assert_raises_with_matching_message(StandardError, /supposed to have an annotation/) do
82
+ s = sheet {
83
+ with shaped_graphic {
84
+ with_content "key"
85
+ }
86
+ }
87
+ PictureApplier.from_sheet(s)
88
+ end
89
+ end
90
+
91
+ def test_from_sheet_key_contents_must_be_recognized
92
+ assert_raises_with_matching_message(StandardError, /does not refer to any graphic/) do
93
+ s = sheet {
94
+ with shaped_graphic {
95
+ with_content "key"
96
+ with_note "missing"
97
+ }
98
+ }
99
+ PictureApplier.from_sheet(s)
100
+ end
101
+ end
102
+
103
+ def test_graphics_are_filtered_by_place
104
+ any_note = "match any note (action would be skip)"
105
+ any_shape_note = "match any shape note (action would be skip)"
106
+ kp = PictureApplier.from_written_intentions(any_note, any_shape_note)
107
+
108
+ matches = lambda do |graphic|
109
+ ints = kp.intentions_that_apply_to(graphic)
110
+ ints.collect { |i| i.source }
111
+ end
112
+
113
+ assert_equal([], matches[shaped_graphic])
114
+ assert_equal([], matches[shaped_graphic {with_content "t"}])
115
+ assert_equal([any_note],
116
+ matches[besheeted(line_graphic { with_note "hi"})])
117
+ assert_equal([any_note, any_shape_note],
118
+ matches[shaped_graphic {with_note "t"}])
119
+ end
120
+
121
+ def test_graphics_can_be_ignored
122
+ skipper = "skip if note says 'ignore'"
123
+ kp = PictureApplier.from_written_intentions(skipper)
124
+
125
+ matches = lambda do |graphic|
126
+ ints = kp.intentions_that_apply_to(graphic)
127
+ ints.collect { |i| i.source }
128
+ end
129
+
130
+ assert_equal([], matches[shaped_graphic])
131
+ assert_equal([], matches[shaped_graphic {with_content "t"}])
132
+ assert_equal([],
133
+ matches[besheeted(line_graphic { with_note "hi"})])
134
+ assert_equal([skipper],
135
+ matches[shaped_graphic {with_note "ignore"}])
136
+ end
137
+
138
+ def test_application_of_intentions
139
+ @applier = PictureApplier.from_sheet(@sheet)
140
+ target = CommandRecorder.new
141
+ @applier.apply(@sheet.graphics_without_labels, target)
142
+ assert_equal(['assert_on_page("page")', 'tests_the_label_text("fred")'],
143
+ target.record)
144
+ end
145
+
146
+ def test_action_must_be_recognized
147
+ assert_raises_with_matching_message(StandardError, /doesn't say what should happen/) do
148
+ s = sheet {
149
+ with shaped_graphic {
150
+ with_content "key"
151
+ with_note "note with friends dines"
152
+ }
153
+ }
154
+ PictureApplier.from_sheet(s)
155
+ end
156
+ end
157
+
158
+ def test_different_lines_can_cause_different_actions
159
+ s = sheet {
160
+ with shaped_graphic {
161
+ graffle_id_is 1
162
+ with_content %Q{page\njohn logs in as "john".}
163
+ }
164
+
165
+ with shaped_graphic {
166
+ with_content "Key"
167
+ with_note %Q{shape content with one word is page name
168
+ otherwise, shape content is user action with args in quotes
169
+ }
170
+ }
171
+ }
172
+ applier = PictureApplier.from_sheet(s)
173
+ target = CommandRecorder.new
174
+ applier.apply(s.graphics_without_labels, target)
175
+ assert_equal(['assert_on_page("page")', 'logs_in_as("john", "john")'],
176
+ target.record)
177
+ end
178
+
179
+ def test_empty_graphics_are_gracefully_ignored
180
+ s = sheet {
181
+ with shaped_graphic
182
+ with line_graphic
183
+ }
184
+
185
+ applier = PictureApplier.from_written_intentions(
186
+ "notes are page names",
187
+ "contents are page names"
188
+ )
189
+
190
+ applier.apply(s.graphics_without_labels, nil)
191
+ end
192
+
193
+ def test_non_empty_graphics_that_do_not_match_are_too
194
+ s = sheet {
195
+ with shaped_graphic { with_content "hi"}
196
+ with line_label {
197
+ for_line 1
198
+ with_content "bye"
199
+ }
200
+ with line_graphic {
201
+ graffle_id_is 1
202
+ }
203
+ }
204
+
205
+ applier = PictureApplier.from_written_intentions(
206
+ "notes are page names"
207
+ )
208
+
209
+ applier.apply(s.graphics_without_labels, nil)
210
+ end
211
+
212
+ def test_lines_that_do_not_match_are_ignored
213
+ s = sheet {
214
+ with shaped_graphic { with_content "only line ignored"}
215
+ with line_label {
216
+ for_line 1
217
+ with_content "multiple lines\n\nare ignored but single are\nnot"
218
+ }
219
+ with line_graphic {
220
+ graffle_id_is 1
221
+ }
222
+ }
223
+
224
+ applier = PictureApplier.from_written_intentions(
225
+ "content lines with one word are page names"
226
+ )
227
+
228
+ target = CommandRecorder.new
229
+ applier.apply(s.graphics_without_labels, target)
230
+ assert_equal(['assert_on_page("not")'],
231
+ target.record)
232
+ end
233
+
234
+
235
+
236
+ def four_counting_shaped_graphics
237
+ sheet {
238
+ with shaped_graphic { with_content "first" }
239
+ with shaped_graphic { with_content "second" }
240
+ with shaped_graphic { with_content "third" }
241
+ with shaped_graphic { with_content "yikes!"}
242
+ }.graphics
243
+ end
244
+
245
+ def four_graphic_contents
246
+ four_counting_shaped_graphics.collect { |g| g.content.as_plain_text }
247
+ end
248
+
249
+
250
+
251
+ class ThriceThenDie
252
+ include Test::Unit::Assertions
253
+
254
+ def initialize
255
+ @count = 0
256
+ end
257
+
258
+ def assert_on_page(value)
259
+ @count += 1
260
+ assert_equal("right", "wrong") if @count >= 4
261
+ end
262
+
263
+ end
264
+
265
+
266
+ def assert_thrice_then_die_log(exc, expected_echoed)
267
+ assert_match(/right.*expected.*was.*wrong/m, exc.message)
268
+ expected = expected_echoed.collect do |w|
269
+ "assert_on_page\\(" + w.inspect + "\\)"
270
+ end.join('.*')
271
+ assert_match(/#{expected}/m, exc.message)
272
+ end
273
+
274
+ def test_logging_prints_list_of_calls_into_target
275
+ @applier = PictureApplier.from_written_intentions(
276
+ "shape content is page name"
277
+ )
278
+ begin
279
+ @applier.apply(four_counting_shaped_graphics, ThriceThenDie.new)
280
+ flunk "never reached"
281
+ rescue Test::Unit::AssertionFailedError => e
282
+ assert_thrice_then_die_log(e, four_graphic_contents)
283
+ end
284
+ end
285
+
286
+ end