taft 0.1.0 → 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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -3
  3. data/examples/ruby/rs/framework/red_sky/api_helpers/general.rb +140 -0
  4. data/examples/ruby/rs/framework/red_sky/api_helpers/rest.rb +249 -0
  5. data/examples/ruby/rs/framework/red_sky/ui_helpers/ui_general.rb +36 -0
  6. data/examples/ruby/rs/framework/red_sky/watir/custom/all.rb +4 -0
  7. data/examples/ruby/rs/framework/red_sky/watir/custom/rs_custom.rb +32 -0
  8. data/examples/ruby/rs/framework/red_sky/watir/flows/flow_objects.rb +466 -0
  9. data/examples/ruby/rs/framework/red_sky/watir/flows/rs_flow_names.rb +15 -0
  10. data/examples/ruby/rs/framework/red_sky/watir/flows/rs_flows.rb +117 -0
  11. data/examples/ruby/rs/framework/red_sky/watir/pages/page_objects.rb +166 -0
  12. data/examples/ruby/rs/framework/red_sky/watir/pages/rs_pages.rb +68 -0
  13. data/examples/ruby/rs/framework/red_sky.rb +12 -0
  14. data/examples/ruby/rs/lib/config/red_sky_config.rb +27 -0
  15. data/examples/ruby/rs/lib/config/runtime_constants.rb +20 -0
  16. data/examples/ruby/rs/lib/red_sky_test_case.rb +218 -0
  17. data/examples/ruby/rs/tests/v1/tc_r001_01_an_example_test.rb +112 -0
  18. data/examples/ruby/rs/tests/v1/tc_r001_01_google_search.rb +64 -0
  19. data/lib/taft_files/framework/zznamezz/api_helpers/general.rb +11 -11
  20. data/lib/taft_files/framework/zznamezz/ui_helpers/ui_general.rb +26 -0
  21. data/lib/taft_files/framework/zznamezz/watir/custom/all.rb +4 -0
  22. data/lib/taft_files/framework/zznamezz/watir/custom/xxabbrevxx_custom.rb +32 -0
  23. data/lib/taft_files/framework/zznamezz/watir/flows/flow_objects.rb +466 -0
  24. data/lib/taft_files/framework/zznamezz/watir/flows/xxabbrevxx_flow_names.rb +15 -0
  25. data/lib/taft_files/framework/zznamezz/watir/flows/xxabbrevxx_flows.rb +117 -0
  26. data/lib/taft_files/framework/zznamezz/watir/pages/page_objects.rb +166 -0
  27. data/lib/taft_files/framework/zznamezz/watir/pages/xxabbrevxx_pages.rb +101 -0
  28. data/lib/taft_files/framework/zznamezz.rb +4 -0
  29. data/lib/taft_files/lib/config/runtime_constants.rb +5 -1
  30. data/lib/taft_files/lib/config/zznamezz_config.rb +7 -2
  31. data/lib/taft_files/lib/zznamezz_test_case.rb +43 -42
  32. data/lib/taft_files/tests/v1/tc_r001_01_an_example_test.rb +1 -1
  33. data/taft.gemspec +4 -4
  34. metadata +28 -5
@@ -0,0 +1,466 @@
1
+ # A class defining the series of Flow classes & helpers
2
+
3
+ # TODO define some base class/classes. Must be scope here for consolidation through inheritance?
4
+
5
+ #require 'minitest/unit' # needed? # TODO use test-unit, or convert everything else to minitest
6
+
7
+ #class FlowObjects
8
+ # attr_accessor :flows
9
+ #
10
+ # def initialize
11
+ # @flows = [] # an array of Flow objects
12
+ # end
13
+ #
14
+ # def add_flow(flow)
15
+ # @flows << flow
16
+ # end
17
+ #
18
+ # # TODO needed?
19
+ # def ==(o)
20
+ # end
21
+ #
22
+ # # TODO needed?
23
+ # def to_s
24
+ # end
25
+ #
26
+ #end
27
+
28
+ # A class defining a flow. This contains the fields to be interacted with, in their proper sequence, with valid or
29
+ # invalid values
30
+ class Flow
31
+
32
+ attr_accessor :name
33
+ attr_accessor :flow_map # some collection of Flow, FlowField & other Flow-type objects # TODO
34
+
35
+ def initialize(name)
36
+ @name = name
37
+ @flow_map = []
38
+ end
39
+
40
+ def add(flow_item)
41
+ @flow_map << flow_item
42
+ end
43
+
44
+ # TODO needed?
45
+ def ==(o)
46
+ end
47
+
48
+ def to_s
49
+ s = ""
50
+ s += "Flow name : #{@name}. Flow map size : #{@flow_map.size}. Flow items :"
51
+ @flow_map.each {|f| s += "\n\t#{f.to_s}" }
52
+ s
53
+ end
54
+
55
+ # Executes the flow
56
+ # Takes symbols which act as flags :
57
+ # success_sym - set to :success for a valid set of inputs, set to :fail for one or more of the inputs to be invalid and
58
+ # the flow to fail
59
+ # mandatory_fields_sym - set to :mandatory to only involve the mandatory fields, set to :all_fields to use all defined
60
+ # fields in the flow
61
+ # custom_values_hash - hash of values some/all of the fields must take (e.g. creating a new record with a specific foreign key)
62
+ # keys are field names (as defined in CeresFlows) in either symbol or string form, values are the values the fields should take
63
+ # TODO : don't want to have to pass in browser each time
64
+ def execute(browser, custom_values_hash = {}, success_sym = :success, mandatory_fields_sym = :mandatory)
65
+ report = nil # if report is still nil at the end of the method, generate a dummy one
66
+ return_value = nil # default value that will be fed into the report # TODO needs to be an array? Or hold multiple
67
+ # values?
68
+ # pass = true
69
+ # all = true
70
+ case success_sym
71
+ when :success
72
+ pass = true
73
+ when :fail
74
+ pass = false
75
+ else
76
+ raise "Did not understand value '#{success_sym.inspect}' for success_sym"
77
+ end
78
+
79
+ case mandatory_fields_sym
80
+ when :mandatory
81
+ all = false
82
+ when :all_fields
83
+ all = true
84
+ else
85
+ raise "Did not understand value '#{mandatory_fields_sym.inspect}' for mandatory_fields_sym"
86
+ end
87
+
88
+ @flow_map.each do |flow_item|
89
+ case flow_item
90
+ when Flow # flows can contain sub-flows
91
+ flow_item.execute(browser, custom_values_hash, success_sym, mandatory_fields_sym) # call recursively
92
+ when FlowPrecondition
93
+ # TODO : find flow from name & execute it
94
+ flow_item.execute_precondition_flow(custom_values_hash)
95
+ when FlowLink
96
+ # debugging
97
+ # puts "Executing #{flow_item.name}"
98
+ # puts browser.url
99
+
100
+ browser.link(:id => flow_item.name).click
101
+
102
+ sleep 1 # TODO determine expected page, call page.wait_until_displayed
103
+ when FlowField
104
+ puts "Now executing field #{flow_item.to_s}"
105
+ next if all == false && flow_item.mandatory == false # skip optional fields if instructed
106
+
107
+ field = nil
108
+
109
+ # TODO : look up field identification parameters (e.g. :id => something) from field definition in rs_pages
110
+ # The linkage should be that the NAME of the rs_page field (e.g. page.add_field("role", :list, :id, "user_role") )
111
+ # matches the NAME of the flow_item. We therefore use flow_item.name to track down the field identification parameters
112
+ # stored in the field definition in rs_pages (in this example, :id, "user_role" )
113
+ # Until this is done, flow_item.name needs to match the rs_pages field ident param.
114
+
115
+ case flow_item.type
116
+ # TODO only have this case statement determine field type, then pass it into one eval line instead of one
117
+ # line per type
118
+ # Type field completion may differ by more than just their field type...
119
+
120
+ when :string
121
+ field = browser.text_field(:id => flow_item.name)
122
+ when :p
123
+ field = browser.p(:id => flow_item.name)
124
+ when :div
125
+ field = browser.div(:id => flow_item.name)
126
+ when :checkbox
127
+ field = browser.input(:id => flow_item.name)
128
+ when :list
129
+ # field = browser.select_list(:id => flow_item.name)
130
+ field = browser.select(:id => flow_item.name)
131
+ when :button
132
+ # buttons only have one function - to be pressed, not to be read or written to
133
+ browser.button(:id => flow_item.name).click
134
+ else
135
+ raise "Cannot execute #{flow_item.class} of type #{flow_item.type}"
136
+ end
137
+ case flow_item.operation
138
+ when :read
139
+ case flow_item.type
140
+ when :string, :checkbox, :list, :p, :div
141
+ # puts "value : #{field.value}"
142
+ # puts "text : #{field.text}"
143
+ return_value = field.value
144
+ return_value = field.text if return_value == "" # p needs .text
145
+ # puts "return_value : #{return_value}"
146
+ end
147
+ when :write
148
+ # get valid value from hash, if it has been specified
149
+ value = custom_values_hash[flow_item.name.to_sym].to_s # flow_item.name is defined in rs_flows to be a string; it is nicer if the custom hash keys are symbols, but we then need to convert them
150
+ value = flow_item.random_valid_value if value == nil
151
+ # value = flow_item.random_invalid_value if invalid # TODO enable
152
+ case flow_item.type
153
+ when :string, :checkbox
154
+ field.set(value)
155
+ when :list
156
+ field.select(value)
157
+ end
158
+ end
159
+ when FlowVerify
160
+ name = "verify"
161
+ verify_flow = Flow.new(name)
162
+ verify_flow.add(flow_item.flow_field)
163
+ # call recursively, have it generate a Report, perform validation against the Report
164
+ report = verify_flow.execute(browser)
165
+ #puts "report : #{report}"
166
+ value = report.value
167
+ flow_item.verify(value)
168
+ return_value = "FlowVerify passed : #{flow_item}"
169
+ when FlowReport # not yet in use - no FlowReports have been defined in rs_flows
170
+ report = flow_item.generate_report
171
+ else
172
+ raise "Cannot execute flow item of class #{flow_item.class}"
173
+ end # end case
174
+ end # end .each
175
+ if report == nil # if report is still nil at the end of the method, generate a dummy one
176
+ # return_value =
177
+ report = FlowReport.new(return_value)
178
+ end
179
+ report
180
+ end # end execute
181
+
182
+ end # end class
183
+
184
+ # A class defining a flow that must be executed as a precondition to the flow this object belongs to
185
+ class FlowPrecondition
186
+
187
+ attr_accessor :precondition_flow # precondition_flow is the name of the precondition flow that must be executed
188
+
189
+ def initialize(precondition_flow)
190
+ @precondition_flow = precondition_flow
191
+ end
192
+
193
+ def to_s
194
+ s = ""
195
+ s += "Precondition Flow : #{@precondition_flow}"
196
+ s
197
+ end
198
+
199
+ # TODO : need success & madatory field flags here?
200
+ def execute_precondition_flow(custom_values_hash = {})
201
+ # find flow from name
202
+ # TODO : flows all stored in @flow (CeresFlow.new()). Gaining access to this feels wrong...
203
+
204
+ # execute flow
205
+
206
+ end
207
+
208
+ end
209
+
210
+ # A class defining a navigation step that is needed as part of a flow. These assume that their navigation is done via
211
+ # links, not buttons/divs/etc.
212
+ class FlowLink
213
+
214
+ attr_accessor :name, :parent_page, :destination, :verification # parent_page is the page within which the desired link
215
+ # can be found
216
+ # destination is the name of the page that the browser will arrive at after performing this navigation.
217
+ # verification is a FlowVerify object that is defined such that it can only pass if it matches the defined field on the
218
+ # destination page
219
+ # TODO enable mechanism such that one can simply state flow.goto(destination) and all flows will be scanned for the
220
+ # flow that will take us there, then that flow will be executed.
221
+
222
+ def initialize(destination, name, parent_page)
223
+ @destination = destination
224
+ @name = name
225
+ @parent_page = parent_page
226
+ end
227
+
228
+ def to_s
229
+ s = ""
230
+ s += "Flow destination : #{@destination}. Link ID : #{@name}. Parent page : #{@parent_page}"
231
+ s
232
+ end
233
+
234
+ end
235
+
236
+ # A class defining a field that is interacted with in some way as part of a flow.
237
+ # Valid types: :string (a text field); :button (a button); :link (a link); :list (a select list); :checkbox (a checkbox)
238
+ # TODO still want link to be valid here? What about FlowLink?
239
+ class FlowField
240
+
241
+ attr_accessor :name, :type, :operation, :mandatory, :size, :custom_valid_value_definition, :custom_invalid_value_definition
242
+
243
+ def initialize(name, type = :string, operation = :write, mandatory = true, size = nil, custom_valid_value_definition = nil, custom_invalid_value_definition = nil)
244
+ raise "FlowField name must be a string" unless name.class == String
245
+ @name = name
246
+ @type = type
247
+ @operation = operation
248
+ raise "Cannot define FlowField #{@name} with operation of #{@operation.inspect}" unless @operation == :read || @operation == :write
249
+ @mandatory = mandatory
250
+ if size == nil
251
+ @size = get_default_size
252
+ else
253
+ @size = size
254
+ end
255
+
256
+ custom_valid_value_definition = nil
257
+ custom_invalid_value_definition = nil
258
+
259
+ check_valid_type
260
+
261
+ # TODO custom_valid_value_definition, etc
262
+ case custom_valid_value_definition
263
+ when NilClass
264
+ # take default valid field def based on @type
265
+ when Symbol
266
+ #run sub-case based on symbol
267
+ when Regexp # ?
268
+ # define method
269
+ when Array
270
+ @custom_valid_value_definition = custom_valid_value_definition
271
+ when block # ?
272
+ # define method
273
+ else
274
+ raise "Could not process custom_valid_value_definition specified for FlowField of name '#{@name}'"
275
+ end
276
+
277
+ case custom_invalid_value_definition
278
+ when NilClass
279
+ # take default valid field def based on @type
280
+ when Symbol
281
+ #run sub-case based on symbol
282
+ when Regexp # ?
283
+ # define method
284
+ when Block # ?
285
+ # define method
286
+ else
287
+ raise "Could not process custom_invalid_value_definition specified for FlowField of name '#{@name}'"
288
+ end
289
+ end
290
+
291
+ # Retrieves default sizes for fields
292
+ # Assumes @type is set
293
+ def get_default_size
294
+ size = 0
295
+ case @type
296
+ when :button, :link, :list, :checkbox
297
+ # do nothing
298
+ when :string
299
+ size = 32
300
+ when :p, :div # these will be read-only so this doesn't really matter
301
+ size = 4000
302
+ else
303
+ raise "#{@type} is not a valid type for FlowField"
304
+ end
305
+ size
306
+ end
307
+
308
+ # Valid types:
309
+ # :string (a text field);
310
+ # :button (a button);
311
+ # :link (a link);
312
+ # :list (a select list);
313
+ # :checkbox (a checkbox)
314
+ def check_valid_type
315
+ case @type
316
+ when :button, :link, :list, :checkbox
317
+ check_valid_size(0)
318
+ when :string, :p, :div
319
+ check_valid_size(@size) # TODO a pointless call - @size will compared against itself!
320
+ else
321
+ raise "#{@type} is not a valid type for FlowField"
322
+ end
323
+ end
324
+
325
+ # Raises unless the supplied size is greater or equal to @size
326
+ def check_valid_size(valid_size_for_type)
327
+ valid = false
328
+ case @size
329
+ when NilClass
330
+ valid = true if @size == nil
331
+ when TrueClass # possible?
332
+ valid = @size if something # TODO
333
+ when FalseClass # possible?
334
+ valid = @size if something # TODO
335
+ when Fixnum, String
336
+ valid = true if @size <= valid_size_for_type
337
+ end
338
+
339
+ raise "Defined size #{@size.inspect} for FlowField '#{@name}' is not valid for field of type '#{type}'" unless valid
340
+ end
341
+
342
+ # TODO needed?
343
+ def ==(o)
344
+ end
345
+
346
+ # TODO needed?
347
+ def to_s
348
+ s = ""
349
+ s += "Flow field : #{@name}. Type : #{@type}. Mandatory : #{@mandatory}. Size : #{@size}"
350
+ s
351
+ end
352
+
353
+ # Generate a random value based on its type
354
+ def random_valid_value
355
+ value = nil
356
+ case @type
357
+ when :string
358
+ value = rand_string(@size) # TODO : vary size of random string?
359
+ when :checkbox
360
+ # value = (rand(2) == 1) # TODO : need to have the object itself have defined what is a valid and invalid
361
+ # value
362
+ value = true # most checkboxes will want to be ticked, but it is plausable that the valid value for some of them
363
+ # is to be unticked
364
+ when :list
365
+ # TODO difficult - need to pick a random item from the list. How do we know its contents?
366
+ # Maybe pick a random number, not greater than the size of the list, then set by index/position?
367
+ if @custom_valid_value_definition != nil # if not nil, custom_valid_value_definition should be an array of the valid options
368
+ value = @custom_valid_value_definition.random
369
+ end
370
+ when :button
371
+ # do nothing
372
+ else
373
+ raise "Do not know how to generate a random valid value for FlowField of type #{@type}"
374
+ end
375
+ value
376
+ end
377
+
378
+ end
379
+
380
+ # A class defining a verification step
381
+ class FlowVerify
382
+
383
+ include Test::Unit::Assertions
384
+
385
+ attr_accessor :expected, :value_or_regex, :flow_field
386
+ # expected is a boolean for whether or not the verification is expected to succeed or fail
387
+ # value_or_regex is a string, number, boolean or regex
388
+ # flow_field is a FlowField object pointing to a field whose value value_or_regex must be used against
389
+
390
+ def initialize(expected, value_or_regex, flow_field)
391
+ @expected = expected
392
+ @value_or_regex = value_or_regex
393
+
394
+ @flow_field = flow_field
395
+ raise ":flow_field must be of class FlowField" unless @flow_field.class == FlowField
396
+ end
397
+
398
+ def to_s
399
+ s = ""
400
+ s += "Flow verifier : expected : #{@expected}. Value/regex : #{@value_or_regex.inspect}. Field : #{@flow_field}"
401
+ s
402
+ end
403
+
404
+ def verify(actual)
405
+ puts "now in verify for FlowVerify for field #{@flow_field} against value #{@value_or_regex}"
406
+
407
+ case @value_or_regex # case is better, leaves room for other options depending on class
408
+ when Regexp
409
+ match = !!(actual =~ @value_or_regex) # double-invert to convert to true-or-false
410
+ else
411
+ match = (actual == @value_or_regex)
412
+ end
413
+ if @expected
414
+ message = "FlowVerify failed. Expected the value to match #{@value_or_regex.inspect} but was actually #{actual.inspect}"
415
+ else
416
+ message = "FlowVerify failed. Expected the value #{@value_or_regex.inspect} to be different to the actual value of #{actual.inspect}"
417
+ end
418
+ puts "about to assert; #{actual.inspect} == #{@value_or_regex.inspect} => #{@expected == match}"
419
+ assert_equal(@expected, match, message)
420
+ puts "assertion passed"
421
+ end
422
+
423
+ end
424
+
425
+ # A class defining feedback to be returned after invoking the flow.
426
+ # TODO : rework this so that it knows how to gather the required information (?)
427
+ class FlowReport
428
+
429
+ # attr_accessor :success, :message, :value_hash_array
430
+ # # success is a boolean
431
+ # # message is a string
432
+ # # value_hash_array is an array of hashes, one per object/event/thing. Its keys are the object's fields, and the
433
+ # values
434
+ ## are their values
435
+ #
436
+ # def initialize(success, message, value_hash_array)
437
+ # @success = success
438
+ # @message = message
439
+ # @value_hash_array = value_hash_array
440
+ # end
441
+ #
442
+ # def to_s
443
+ # s = ""
444
+ # s += "Flow report : success? #{@success}. Message : #{@message}. Values : #{@value_hash_array}"
445
+ # s
446
+ # end
447
+
448
+ attr_accessor :value
449
+
450
+ def initialize(value = nil)
451
+ # do nothing?
452
+ @value = value
453
+ end
454
+
455
+ def to_s
456
+ s = ""
457
+ s += "Flow report : value : #{@value}"
458
+ s
459
+ end
460
+
461
+ def generate_report
462
+ #TODO
463
+ ""
464
+ end
465
+
466
+ end
@@ -0,0 +1,15 @@
1
+ # List of constants that store names of flows
2
+ # Names are stored as symbols
3
+
4
+ class RSFN
5
+
6
+ # Homepage
7
+ GOTO_HOMEPAGE = :goto_homepage
8
+
9
+ # Users
10
+ VIEW_ALL_USERS = :view_all_users
11
+ VIEW_USER = :view_user
12
+ CREATE_USER = :create_user
13
+ DELETE_USER = :delete_user
14
+
15
+ end
@@ -0,0 +1,117 @@
1
+ # A class defining the UI flows within RedSky, such that simple usecase-esq methods arise from them and can
2
+ # perform a series of UI actions (making a record, viewing & deleting, etc.)
3
+
4
+ # The intended use is that the test (or supporting framework) call @flow.flow_name, e.g. @flow.create_project, which will
5
+ # perform all of the actions of that flow. Parameters can be supplied to the call, which will change its behaviour
6
+
7
+ require_relative 'flow_objects'
8
+ require_relative 'rs_flow_names'
9
+
10
+ class RSFlows
11
+
12
+ attr_accessor :flows # array of Flow objects
13
+ attr_accessor :flow_names # array of names of known Flow objects
14
+
15
+
16
+ # Assembles all of the flows and stores them, ready for use
17
+ def initialize
18
+ @flows = [] # an array of Flow objects
19
+ @flow_names = []
20
+
21
+ # Simple nav flow to get to the homepage
22
+ flow = Flow.new(RSFN::GOTO_HOMEPAGE)
23
+ flow.add(FlowLink.new("goto_homepage", "rs_home_header_link", nil)) # no parent page, is valid from any page
24
+ add_flow(flow)
25
+
26
+ # View all users
27
+ flow = new_base_flow(RSFN::VIEW_ALL_USERS)
28
+ flow.add(FlowLink.new("all_users", "users_header_link", nil))
29
+ add_flow(flow)
30
+
31
+ # Create user
32
+ flow = new_base_flow(RSFN::CREATE_USER)
33
+ add_existing_flow_to_flow(flow, RSFN::VIEW_ALL_USERS)
34
+
35
+ flow.add(FlowLink.new("all_users", "users_header_link", nil))
36
+ flow.add(FlowLink.new("create_user", "new_user_link", "all_users"))
37
+ field_flow = Flow.new("create_user_fields")
38
+
39
+ field_flow.add(FlowField.new("user_name"))
40
+ field_flow.add(FlowField.new("user_role", :list, :write, true, nil, RedSkyConfig::ALL_USER_ROLES))
41
+
42
+ flow.add(field_flow)
43
+ flow.add(FlowField.new("save", :button))
44
+
45
+ message = FlowField.new("notice", :p, :read)
46
+ flow.add(FlowVerify.new(true, "User was successfully created.", message))
47
+
48
+ add_flow(flow)
49
+ end
50
+
51
+
52
+ ##############################################################################
53
+
54
+
55
+ def method_missing(name, *args, &block)
56
+ puts "RSFlows method_missing called; name = #{name.inspect}; #{name.class}"
57
+
58
+ if flow_known(name)
59
+ puts "Flow #{name} is known"
60
+ # TODO define a whole bunch of methods and then perform them
61
+ # If args and/or block have been provided, process them. E.g. one arg could be a trigger to perform the flow with
62
+ # invalid values # TODO : is that the best way of doing that?
63
+ else
64
+ super
65
+ end
66
+ end
67
+
68
+ def add_flow(flow)
69
+ @flows << flow
70
+ @flow_names << flow.name
71
+ end
72
+
73
+ # TODO needed?
74
+ def ==(o)
75
+ end
76
+
77
+ # TODO needed?
78
+ def to_s
79
+ s = ""
80
+ s += "#{@flows.size} flows defined. Names :"
81
+ @flow_names.each {|f| s += "\n#{f}" }
82
+ s
83
+ end
84
+
85
+ # Will convert name to a string
86
+ def flow_known?(name)
87
+ @flow_names.include?(name)
88
+ end
89
+
90
+ # Retrieves the specific flow; raises if it cannot be found
91
+ # Will convert name to a string
92
+ def find(name)
93
+ raise "Could not locate flow '#{name}'" unless flow_known?(name)
94
+ @flows[@flow_names.index(name)]
95
+ end
96
+
97
+ # Finds & executes a flow
98
+ def find_and_execute(browser, name, custom_values_hash = {}, success_sym = :success, mandatory_fields_sym = :mandatory)
99
+ f = find(name)
100
+ f.execute(browser, custom_values_hash, success_sym, mandatory_fields_sym)
101
+ end
102
+
103
+ # Adds an already-existing flow to the supplied flow
104
+ def add_existing_flow_to_flow(new_flow, existing_flow_name)
105
+ # TODO : need deduplication mechanism so that a flow doesn't gain two/more duplicate flow items in a row (e.g. two calls to GOTO_HOMEPAGE in a row)
106
+ new_flow.add(find(existing_flow_name))
107
+ end
108
+
109
+ # Shortcut to define a new flow with standard prerequisite flows already added
110
+ # Cannot be called until the flows that are to be added have been defined
111
+ def new_base_flow(name)
112
+ flow = Flow.new(name)
113
+ add_existing_flow_to_flow(flow, RSFN::GOTO_HOMEPAGE)
114
+ flow
115
+ end
116
+
117
+ end