graffle 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -0
- data/Manifest.txt +15 -8
- data/Rakefile.hoe +1 -2
- data/bin/interpret-intentions.rb +51 -0
- data/examples/objects with notes.rb +1 -0
- data/examples/old-style-rails-workflow-test.expected +11 -0
- data/examples/old-style-rails-workflow-test.rb +43 -0
- data/examples/rails-workflow-test.rb +7 -6
- data/examples/sheet with key.expected +5 -0
- data/examples/sheet with key.graffle +0 -0
- data/examples/sheet with key.rb +38 -0
- data/graffle.tmproj +242 -75
- data/lib/graffle.rb +11 -2
- data/lib/graffle/nodoc/hacks.rb +1 -0
- data/lib/graffle/point.rb +1 -0
- data/lib/graffle/stereotypes.rb +34 -3
- data/lib/graffle/version.rb +3 -1
- data/lib/graphical_tests_for_rails.rb +11 -9
- data/lib/graphical_tests_for_rails/jumbled-string-canonicalizer.rb +117 -0
- data/lib/graphical_tests_for_rails/orderings.rb +1 -2
- data/lib/graphical_tests_for_rails/picture-applier.rb +257 -0
- data/lib/graphical_tests_for_rails/stereotype-extensions.rb +111 -0
- data/lib/graphical_tests_for_rails/text-appliers.rb +10 -84
- data/lib/graphical_tests_for_rails/user-intention.rb +250 -0
- data/test/abstract-graphic-tests.rb +10 -0
- data/test/container-tests.rb +29 -0
- data/test/graphical_tests_for_rails/jumbled-string-canonicalizer-tests.rb +174 -0
- data/test/graphical_tests_for_rails/new-style-picture-applier-tests.rb +286 -0
- data/test/graphical_tests_for_rails/old-style-picture-applier-tests.rb +129 -0
- data/test/graphical_tests_for_rails/user-intention-tests.rb +389 -0
- data/test/graphical_tests_for_rails/util.rb +9 -0
- data/test/sheet-tests.rb +21 -0
- data/test/text-tests.rb +6 -0
- metadata +25 -26
- data/design-notes/graphical-tests-for-rails-objects.graffle +0 -644
- data/lib/graphical_tests_for_rails/graphic-volunteers.rb +0 -75
- data/lib/graphical_tests_for_rails/picture-appliers.rb +0 -225
- data/lib/graphical_tests_for_rails/volunteer-pool.rb +0 -115
- data/test/graphical_tests_for_rails/deprecated-graphic-interpreter-tests.rb +0 -121
- data/test/graphical_tests_for_rails/graphic-volunteer-tests.rb +0 -218
- data/test/graphical_tests_for_rails/picture-applier-tests.rb +0 -215
- data/test/graphical_tests_for_rails/text-applier-tests.rb +0 -111
data/lib/graffle.rb
CHANGED
@@ -38,7 +38,7 @@ module Graffle
|
|
38
38
|
filename = File.join(filename, "data.plist")
|
39
39
|
end
|
40
40
|
|
41
|
-
parse_xml(
|
41
|
+
parse_xml(data_from(filename))
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.parse_xml(content)
|
@@ -46,7 +46,16 @@ module Graffle
|
|
46
46
|
Document.takes_on(doc)
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
|
+
def self.data_from(filename)
|
51
|
+
possible_gzip_header = File.read(filename, 2)
|
52
|
+
if possible_gzip_header.unpack('CC') == [0x1f, 0x8b]
|
53
|
+
`gunzip < '#{filename}'`
|
54
|
+
else
|
55
|
+
File.read(filename)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
50
59
|
def self.stereotype(o) # :nodoc:
|
51
60
|
return true if ShapedGraphic.takes_on(o)
|
52
61
|
return true if LineGraphic.takes_on(o)
|
data/lib/graffle/nodoc/hacks.rb
CHANGED
data/lib/graffle/point.rb
CHANGED
data/lib/graffle/stereotypes.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
#
|
3
3
|
# Created by Brian Marick on 2007-07-06.
|
4
4
|
# Copyright (c) 2007. All rights reserved.
|
5
|
+
"prevent the above from infecting rdoc"
|
6
|
+
|
5
7
|
|
6
8
|
module Graffle
|
7
9
|
module Builders # :nodoc:
|
@@ -132,8 +134,16 @@ module Graffle
|
|
132
134
|
# Pro feature)
|
133
135
|
def has_note?; self.has_key?('Notes'); end
|
134
136
|
alias_method :has_notes?, :has_note?
|
137
|
+
|
138
|
+
# Delete this graphic from inside its Container (a Sheet or a
|
139
|
+
# Group). The name is delete_yourself to avoid overriding the
|
140
|
+
# delete method on the underlying class.
|
141
|
+
def delete_yourself
|
142
|
+
container.delete_graphic(self)
|
143
|
+
end
|
135
144
|
|
136
|
-
|
145
|
+
|
146
|
+
|
137
147
|
end
|
138
148
|
|
139
149
|
|
@@ -338,11 +348,11 @@ module Graffle
|
|
338
348
|
if label
|
339
349
|
label.content.as_lines
|
340
350
|
else
|
341
|
-
label || []
|
351
|
+
label || [] # TODO: Remove unnecessary first condition.
|
342
352
|
end
|
343
353
|
end
|
344
354
|
|
345
|
-
|
355
|
+
|
346
356
|
private
|
347
357
|
def points_at(*points)
|
348
358
|
self['Points'] = points.collect { |p| "{#{p[0]}, #{p[1]}}" }
|
@@ -379,6 +389,16 @@ module Graffle
|
|
379
389
|
o.container = self # works because instance_evaled.
|
380
390
|
end
|
381
391
|
end
|
392
|
+
|
393
|
+
# Delete a graphic from inside this Container
|
394
|
+
# The name is delete_graphic to avoid overriding the
|
395
|
+
# delete method on the underlying class.
|
396
|
+
def delete_graphic(graphic)
|
397
|
+
graphic.container = nil
|
398
|
+
graphics.delete(graphic)
|
399
|
+
end
|
400
|
+
|
401
|
+
|
382
402
|
|
383
403
|
# Find the AbstractGraphic matching the integer id. Returns nil
|
384
404
|
# if not found.
|
@@ -396,6 +416,15 @@ module Graffle
|
|
396
416
|
end
|
397
417
|
result
|
398
418
|
end
|
419
|
+
|
420
|
+
# Find a graphic whose ShapedGraphic#content matches the given
|
421
|
+
# _string_.
|
422
|
+
def find_by_content(string)
|
423
|
+
result = graphics.find do | elt |
|
424
|
+
elt.respond_to?(:content) &&
|
425
|
+
elt.content.as_plain_text.downcase === string.downcase
|
426
|
+
end
|
427
|
+
end
|
399
428
|
|
400
429
|
def self.stereotype_children_of(parent) # :nodoc:
|
401
430
|
parent.graphics.each do | child |
|
@@ -409,6 +438,7 @@ module Graffle
|
|
409
438
|
end
|
410
439
|
true
|
411
440
|
end
|
441
|
+
|
412
442
|
|
413
443
|
end
|
414
444
|
|
@@ -498,6 +528,7 @@ module Graffle
|
|
498
528
|
# Null object for text
|
499
529
|
class Null # :nodoc:
|
500
530
|
def as_lines; []; end
|
531
|
+
def as_plain_text; ''; end
|
501
532
|
end
|
502
533
|
|
503
534
|
end
|
data/lib/graffle/version.rb
CHANGED
@@ -5,23 +5,25 @@
|
|
5
5
|
|
6
6
|
|
7
7
|
require 'graffle'
|
8
|
+
require 'graphical_tests_for_rails/jumbled-string-canonicalizer'
|
8
9
|
require 'graphical_tests_for_rails/orderings'
|
10
|
+
require 'graphical_tests_for_rails/picture-applier'
|
11
|
+
require 'graphical_tests_for_rails/stereotype-extensions'
|
9
12
|
require 'graphical_tests_for_rails/text-appliers'
|
10
|
-
require 'graphical_tests_for_rails/
|
11
|
-
require 'graphical_tests_for_rails/graphic-volunteers'
|
12
|
-
require 'graphical_tests_for_rails/volunteer-pool'
|
13
|
+
require 'graphical_tests_for_rails/user-intention'
|
13
14
|
|
14
15
|
# Classes within this module help you build up Rails integration tests
|
15
16
|
# from OmniGraffle files. The important classes are these:
|
16
17
|
#
|
17
|
-
# *
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# * PictureApplier: These objects are 'programmed' with unstructured
|
19
|
+
# descriptions of how they should handle particular lines in particular
|
20
|
+
# kinds of Graffle objects. Once programmed, they are handed Graffle
|
21
|
+
# objects, turn them into messages (and arguments), and send those messages
|
22
|
+
# to some _target_.
|
23
|
+
# * GraphicArrayOrderings::GraphicOrderer: These objects take a list of graphics in no
|
21
24
|
# particular order and put it in the right order for a particular
|
22
|
-
# style of test. For example, an InWorkflowOrder represents an
|
25
|
+
# style of test. For example, an GraphicArrayOrderings::InWorkflowOrder represents an
|
23
26
|
# order created by tracing through shapes connected by lines.
|
24
|
-
|
25
27
|
module GraphicalTestsForRails
|
26
28
|
include GraphicArrayOrderings
|
27
29
|
include TextAppliers
|
@@ -0,0 +1,117 @@
|
|
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 'ostruct'
|
7
|
+
require 's4t-utils'
|
8
|
+
|
9
|
+
module GraphicalTestsForRails
|
10
|
+
# A JumbledStringCanonicalizer takes a string of words and matches it
|
11
|
+
# against a set of patterns, returning strings that represent each match.
|
12
|
+
# All of the regular expressions in the array must match for the corresponding
|
13
|
+
# string to be returned.
|
14
|
+
class JumbledStringCanonicalizer
|
15
|
+
|
16
|
+
# The _patterns_ are a hash mapping arrays of regular expressions to strings.
|
17
|
+
# The block can be used to change the default. Messages within the block are
|
18
|
+
# sent to this JumbledStringCanonicalizer. (The block is instance_eval'd.)
|
19
|
+
def initialize(patterns = {}, &block) # :yields:
|
20
|
+
@patterns = patterns
|
21
|
+
@no_match_message = proc {|source| "Cannot recognize anything in '#{source}'"}
|
22
|
+
@multiple_match_message = proc do |source, pretty_reasons, raw_reasons|
|
23
|
+
"Multiple matches in '#{source}':\n" +
|
24
|
+
pretty_reasons.join("\n")
|
25
|
+
end
|
26
|
+
instance_eval(&block) if block
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set a default value to return when there's no match. The default
|
30
|
+
# behavior is to raise a StandardError exception.
|
31
|
+
def default(value)
|
32
|
+
@default_given = true
|
33
|
+
@default = value
|
34
|
+
end
|
35
|
+
|
36
|
+
# If no match was found and there's no default value, the _block_ is
|
37
|
+
# called with the failing string as its only argument. That block should
|
38
|
+
# produce a string that's set as the raised StandardError's message.
|
39
|
+
def no_match_message(&block) # :yields: failing_string
|
40
|
+
@no_match_message = block
|
41
|
+
end
|
42
|
+
|
43
|
+
# If more than one of the arrays matches a string, and there's no disambiguator,
|
44
|
+
# the _block_ is called with the failing string as its only argument. That block should
|
45
|
+
# produce a string that's set as the raised StandardError's message.
|
46
|
+
def multiple_match_message(&block) # :yields: failing_string
|
47
|
+
@multiple_match_message = block
|
48
|
+
end
|
49
|
+
|
50
|
+
# This method takes a _block_ like the ones passed to Array#sort (two arguments,
|
51
|
+
# a return value that's 1, 0, or -1). If there are multiple matches for a given
|
52
|
+
# string, they are sorted and the first one is chosen. If there is no
|
53
|
+
# disambiguator, StandardError is raised.
|
54
|
+
def disambiguate(&block) # :yields: match1, match2
|
55
|
+
@disambiguator = block
|
56
|
+
end
|
57
|
+
|
58
|
+
# Take a string, match it against one of the keys in the _patterns_ given to
|
59
|
+
# JumbledStringCanonicalizer.new, and return an informative object. That object
|
60
|
+
# responds to these messages:
|
61
|
+
# * value: the string value corresponding to the match.
|
62
|
+
# * source: the original _string_ (unchanged)
|
63
|
+
# * raw_reason: an array. Each element is a list of the groups captured
|
64
|
+
# by one of the regular expressions in the match, but with any nil
|
65
|
+
# groups compacted out.
|
66
|
+
# * pretty_reason: the raw_reason converted into a tolerably understandable
|
67
|
+
# string.
|
68
|
+
# If there
|
69
|
+
# are zero matches or more than one match, the default behavior is to raise StandardError.
|
70
|
+
def canonicalize(string)
|
71
|
+
raw_reasons = []
|
72
|
+
matches = @patterns.keys.collect do |k|
|
73
|
+
raw_reasons = []
|
74
|
+
if k.all? { |r| r =~ string && raw_reasons << $~.captures.compact }
|
75
|
+
OpenStruct.new(:value => @patterns[k], :source => string,
|
76
|
+
:raw_reason => raw_reasons,
|
77
|
+
:pretty_reason => prettify(raw_reasons))
|
78
|
+
end
|
79
|
+
end.compact
|
80
|
+
case matches.length
|
81
|
+
when 0: error_or_default(string)
|
82
|
+
when 1: matches.first
|
83
|
+
else error_or_disambiguation(string, matches)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def prettify(raw_reasons)
|
90
|
+
clauses = raw_reasons.collect do |partial_list|
|
91
|
+
S4tUtils.friendly_list("and", partial_list)
|
92
|
+
end
|
93
|
+
clauses.join("; and also ")
|
94
|
+
end
|
95
|
+
|
96
|
+
def quote(text)
|
97
|
+
%Q{"#{text}"}
|
98
|
+
end
|
99
|
+
|
100
|
+
def error_or_default(string)
|
101
|
+
if @default_given
|
102
|
+
OpenStruct.new(:value => @default, :source => string,
|
103
|
+
:raw_reason => [],
|
104
|
+
:pretty_reason => "no matching words")
|
105
|
+
else
|
106
|
+
user_is_bewildered(@no_match_message.call(string))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def error_or_disambiguation(string, matches)
|
111
|
+
return matches.sort(&@disambiguator).first if @disambiguator
|
112
|
+
user_is_bewildered(@multiple_match_message.call(
|
113
|
+
string, matches.collect { |m| m.pretty_reason },
|
114
|
+
matches.collect { |m| m.raw_reason }))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created by Brian Marick on 2007-07-22.
|
4
|
+
# Copyright (c) 2007. All rights reserved.
|
5
|
+
|
6
|
+
|
7
|
+
require 'graphical_tests_for_rails/text-appliers'
|
8
|
+
require 'graphical_tests_for_rails/user-intention'
|
9
|
+
|
10
|
+
require 'enumerator'
|
11
|
+
require 'test/unit/assertionfailederror'
|
12
|
+
require 'ostruct'
|
13
|
+
|
14
|
+
|
15
|
+
module GraphicalTestsForRails
|
16
|
+
|
17
|
+
# A PictureApplier takes a list of strings and deduces what those strings
|
18
|
+
# say about how to generate message sends from graphics. It can then be
|
19
|
+
# given a list of AbstractGraphic objects to send those messages to some
|
20
|
+
# target object.
|
21
|
+
#
|
22
|
+
# Here are typical strings:
|
23
|
+
# "ignore shape content"
|
24
|
+
# "line label content is user action with arguments in quotes"
|
25
|
+
# "annotations with ellipses are page names"
|
26
|
+
# "in shape content with ellipses, ignore lines with one word"
|
27
|
+
#
|
28
|
+
# Here's what strings can describe. Unless noted, words don't have to be
|
29
|
+
# in any particular order. You can generally use either singular or
|
30
|
+
# plural forms.
|
31
|
+
# * kind of graphic. It can be a _line_, a <i>line label</i>, or a
|
32
|
+
# _shape_ (alt. <i>shaped graphic</i>). Groups are not currently
|
33
|
+
# allowed. The kind of graphic can be omitted, in which case
|
34
|
+
# the string matches all kinds.
|
35
|
+
# * where the text of interest lives. It can be _note_ (alt. _annotation_)
|
36
|
+
# or _content_. (The content is the text that appears on the normal
|
37
|
+
# OmniGraffle screen. The note appears if you hover your cursor above
|
38
|
+
# it or look at Notes in the Inspector.) One of these words must be present.
|
39
|
+
# * An action to take:
|
40
|
+
# * _skip_ (alt. _ignore_). Do not turn text into message sends.
|
41
|
+
# * <i>user action</i> (in that order) followed by <i>quoted</i> and
|
42
|
+
# <i>arg</i> (in any order). Variations on _quoted_ and _arg_ are
|
43
|
+
# allowed. For example, "quote all arguments" matches. This argument
|
44
|
+
# means that <i>Fred controls the "widget"</i> is turned into:
|
45
|
+
# target.controls_the("fred", "widget")
|
46
|
+
# Strings to match are downcased
|
47
|
+
# before the match and non-word-characters are stripped, so "John runs AWAY!"
|
48
|
+
# turns into runs_away("john").
|
49
|
+
# * _claim_ followed by _quoted_ and _arg_, as above. In this case,
|
50
|
+
# <i>Fred controls the widget</i> is turned into:
|
51
|
+
# target.fred_controls_the("widget")
|
52
|
+
# * _name_ and _pages_ in any order. _HOME_ is converted into
|
53
|
+
# target.assert_on_page("home").
|
54
|
+
# The string must describe an action.
|
55
|
+
# * A description of which lines within a body of text should have the
|
56
|
+
# action applied to it.
|
57
|
+
# * <i>with one word</i> (in that order, possibly with other words
|
58
|
+
# interspersed). The action only happens on lines of text that contain
|
59
|
+
# a single word.
|
60
|
+
# * <i>text...</i> (alt., _ellipses_). The action only happens on lines
|
61
|
+
# of text that end with ellipses.
|
62
|
+
# * <i>otherwise</i> (alt <i>other kinds of</i>, <i>other kind of</i>).
|
63
|
+
# The action happens for all lines. (Only useful when it follows one
|
64
|
+
# of the above.)
|
65
|
+
#
|
66
|
+
# These descriptions are checked in the order given:
|
67
|
+
#
|
68
|
+
# one word lines in shape content name pages.
|
69
|
+
# shape content lines with ellipses are claims with quoted args.
|
70
|
+
# otherwise, ignore shape content.
|
71
|
+
#
|
72
|
+
# There's one special case to handle a common desire: turning off
|
73
|
+
# all interpretation of a graphic. Typically that's used for shape
|
74
|
+
# contents or line labels that are just there for the human reader, not
|
75
|
+
# for the test. To ignore them, add a note containing the word "ignore"
|
76
|
+
# and this text as the first description:
|
77
|
+
#
|
78
|
+
# skip when note contains 'ignore'
|
79
|
+
#
|
80
|
+
|
81
|
+
class PictureApplier
|
82
|
+
|
83
|
+
attr_reader :intentions
|
84
|
+
|
85
|
+
def initialize(intentions = [], &block) #:nodoc:
|
86
|
+
@intentions = intentions
|
87
|
+
@log = []
|
88
|
+
|
89
|
+
if block
|
90
|
+
STDERR.puts "Warning: old-style PictureApplier initialization is deprecated."
|
91
|
+
instance_eval(&block)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Apply messages derived from the _graphics_ to the _target_, adding
|
96
|
+
# descriptions of each message to the _log_.
|
97
|
+
def apply(graphics, target, log=[])
|
98
|
+
return old_style_apply(graphics, target, log) if @intentions.empty?
|
99
|
+
|
100
|
+
while_logging_for_test_errors do
|
101
|
+
graphics.each { |g| apply_one(g, target) }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Use the Key graphic in the _sheet_ to program the PictureApplier's
|
106
|
+
# behavior. The Key graphic is then discarded (not used in the actual
|
107
|
+
# test.)
|
108
|
+
def self.from_sheet(sheet, *default_written_intentions)
|
109
|
+
key = sheet.find_by_content("key")
|
110
|
+
|
111
|
+
if key.nil?
|
112
|
+
user_denies(default_written_intentions.empty?) {
|
113
|
+
"The sheet has no key for the tests. It needs a graphic whose visible content is 'key'."
|
114
|
+
}
|
115
|
+
from_written_instructions(*default_written_intentions)
|
116
|
+
else
|
117
|
+
user_claims(key.has_notes?) {
|
118
|
+
"The key graphic is supposed to have an annotation describing how to interpret the rest of the sheet."
|
119
|
+
}
|
120
|
+
key.delete_yourself
|
121
|
+
from_graphic(key)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# The _graphic_ should have an annotation with a list of written
|
126
|
+
# instructions of the form accepted by from_written_instructions.
|
127
|
+
def self.from_graphic(graphic)
|
128
|
+
written_intentions = graphic.notes.as_lines.find_all { |l| l.strip != "" }
|
129
|
+
from_written_intentions(*written_intentions)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Accept a list of written instructions that are later obeyed by
|
133
|
+
# apply.
|
134
|
+
def self.from_written_intentions(*written_intentions)
|
135
|
+
intentions = written_intentions.collect do |line|
|
136
|
+
UserIntention.understand(line)
|
137
|
+
end
|
138
|
+
new(intentions)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Synonym for from_written_intentions.
|
142
|
+
def self.from_written_instructions(*args)
|
143
|
+
self.from_written_intentions(*args)
|
144
|
+
end
|
145
|
+
|
146
|
+
include GraphicalTestsForRails::TextAppliers
|
147
|
+
|
148
|
+
# Claim that the PictureApplier matches graphics matching
|
149
|
+
# the _form_description_, a string. Here are the form descriptions
|
150
|
+
# currently supported:
|
151
|
+
#
|
152
|
+
# * 'shaped graphic content': A shaped graphic is a rectangle, oval,
|
153
|
+
# embedded picture, etc.: anything that is neither a line nor a
|
154
|
+
# group. The content is the text you type in when you double-click
|
155
|
+
# on the object. When given the form description, the PictureApplier
|
156
|
+
# will match any shaped graphic that contains text.
|
157
|
+
# * 'line label content': This matches any line that has a label.
|
158
|
+
# (Note: lines can have more than one label. That's not handled.)
|
159
|
+
# * 'note': Matches any object that contains an annotation. (Annotations are
|
160
|
+
# only available in OmniGraffle Pro. They're arbitrary text
|
161
|
+
# associated with a line or shaped graphic. You can see the
|
162
|
+
# annotations if you hover over the object.)
|
163
|
+
#
|
164
|
+
# The _form_description_ describes the first of two matching steps.
|
165
|
+
# If it matches, it extracts the appropriate text (content or
|
166
|
+
# annotation) and hands it to the next step, described by given.
|
167
|
+
#
|
168
|
+
# This method returns self so that other methods can be chained onto it.
|
169
|
+
def given(form_description) # :nodoc:
|
170
|
+
@new_style_place_implied = form_description
|
171
|
+
@new_style_text_implied = ""
|
172
|
+
@new_style_action_implied = ""
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
# Claim that the PictureApplier matches particular text extracted from
|
177
|
+
# a graphical object. These _text_descriptions_ are currently supported:
|
178
|
+
#
|
179
|
+
# * 'text...': match if the text ends in ellipses (ignoring whitespace).
|
180
|
+
# * 'ignore': match if the text ends in the word "ignore" (ignoring whitespace).
|
181
|
+
#
|
182
|
+
# This method returns self so that other methods can be chained onto it.
|
183
|
+
def matching(text_description) # :nodoc:
|
184
|
+
@new_style_text_implied = text_description
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# When a PictureApplier matches an object, the extracted text is
|
189
|
+
# passed to the _text_applier_ to be turned into messages.
|
190
|
+
def use(text_applier) # :nodoc:
|
191
|
+
new_style_action_implied =
|
192
|
+
case text_applier
|
193
|
+
when TextApplierThatDoesNothing: 'skip'
|
194
|
+
when PrefixNameAndArgsFromQuotedText: 'user action with args from quoted text'
|
195
|
+
when ArgsFromQuotedText: "claim with args from quoted text"
|
196
|
+
when TextAsNameOfPage: 'name of page'
|
197
|
+
else user_is_bewildered("Unknown class: #{text_applier.class}")
|
198
|
+
end
|
199
|
+
parts = [new_style_action_implied, @new_style_place_implied, @new_style_text_implied]
|
200
|
+
@intentions << UserIntention.understand(parts.join(" "))
|
201
|
+
end
|
202
|
+
|
203
|
+
# After a PictureApplier matches a graphical object, it's ignored.
|
204
|
+
# This is used to prevent text associated with the object from being
|
205
|
+
# turned into messages.
|
206
|
+
def skip # :nodoc:
|
207
|
+
use(TextApplierThatDoesNothing.new)
|
208
|
+
end
|
209
|
+
|
210
|
+
def intentions_that_apply_to(graphic) # :nodoc:
|
211
|
+
@intentions.find_all do |i|
|
212
|
+
# pi i, '#'
|
213
|
+
# if (pi i.applies_to_graphic?(graphic), 'applies to graphic?')
|
214
|
+
# pi i.lines_from(graphic), "graphic contains"
|
215
|
+
# i.applies_to_text?(i.lines_from(graphic))
|
216
|
+
# end
|
217
|
+
i.applies_to_graphic?(graphic) &&
|
218
|
+
i.applies_to_text?(i.lines_from(graphic))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
|
227
|
+
def apply_one(graphic, target)
|
228
|
+
relevant_intentions = intentions_that_apply_to(graphic)
|
229
|
+
return if relevant_intentions.empty?
|
230
|
+
|
231
|
+
lines_chosen = relevant_intentions.first.lines_from(graphic)
|
232
|
+
lines_chosen.each do |line|
|
233
|
+
intention_for_line = relevant_intentions.find do |i|
|
234
|
+
i.applies_to_line?(line)
|
235
|
+
end
|
236
|
+
intention_for_line.apply(line, target, @log) if intention_for_line
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
|
242
|
+
def while_logging_for_test_errors
|
243
|
+
begin
|
244
|
+
yield
|
245
|
+
rescue Test::Unit::AssertionFailedError, StandardError, ScriptError => e
|
246
|
+
new_message = %Q{#{e.message}. That happened during or after the last of these commands:\n#{@log.join("\n")}}
|
247
|
+
new_e = e.exception(new_message)
|
248
|
+
new_e.set_backtrace(e.backtrace)
|
249
|
+
raise new_e
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|