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.
- 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
|