taft 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -3
- data/examples/ruby/rs/framework/red_sky/api_helpers/general.rb +140 -0
- data/examples/ruby/rs/framework/red_sky/api_helpers/rest.rb +249 -0
- data/examples/ruby/rs/framework/red_sky/ui_helpers/ui_general.rb +36 -0
- data/examples/ruby/rs/framework/red_sky/watir/custom/all.rb +4 -0
- data/examples/ruby/rs/framework/red_sky/watir/custom/rs_custom.rb +32 -0
- data/examples/ruby/rs/framework/red_sky/watir/flows/flow_objects.rb +466 -0
- data/examples/ruby/rs/framework/red_sky/watir/flows/rs_flow_names.rb +15 -0
- data/examples/ruby/rs/framework/red_sky/watir/flows/rs_flows.rb +117 -0
- data/examples/ruby/rs/framework/red_sky/watir/pages/page_objects.rb +166 -0
- data/examples/ruby/rs/framework/red_sky/watir/pages/rs_pages.rb +68 -0
- data/examples/ruby/rs/framework/red_sky.rb +12 -0
- data/examples/ruby/rs/lib/config/red_sky_config.rb +27 -0
- data/examples/ruby/rs/lib/config/runtime_constants.rb +20 -0
- data/examples/ruby/rs/lib/red_sky_test_case.rb +218 -0
- data/examples/ruby/rs/tests/v1/tc_r001_01_an_example_test.rb +112 -0
- data/examples/ruby/rs/tests/v1/tc_r001_01_google_search.rb +64 -0
- data/lib/taft_files/framework/zznamezz/api_helpers/general.rb +11 -11
- data/lib/taft_files/framework/zznamezz/ui_helpers/ui_general.rb +26 -0
- data/lib/taft_files/framework/zznamezz/watir/custom/all.rb +4 -0
- data/lib/taft_files/framework/zznamezz/watir/custom/xxabbrevxx_custom.rb +32 -0
- data/lib/taft_files/framework/zznamezz/watir/flows/flow_objects.rb +466 -0
- data/lib/taft_files/framework/zznamezz/watir/flows/xxabbrevxx_flow_names.rb +15 -0
- data/lib/taft_files/framework/zznamezz/watir/flows/xxabbrevxx_flows.rb +117 -0
- data/lib/taft_files/framework/zznamezz/watir/pages/page_objects.rb +166 -0
- data/lib/taft_files/framework/zznamezz/watir/pages/xxabbrevxx_pages.rb +101 -0
- data/lib/taft_files/framework/zznamezz.rb +4 -0
- data/lib/taft_files/lib/config/runtime_constants.rb +5 -1
- data/lib/taft_files/lib/config/zznamezz_config.rb +7 -2
- data/lib/taft_files/lib/zznamezz_test_case.rb +43 -42
- data/lib/taft_files/tests/v1/tc_r001_01_an_example_test.rb +1 -1
- data/taft.gemspec +4 -4
- metadata +28 -5
@@ -5,22 +5,22 @@ require "avro"
|
|
5
5
|
require "csv"
|
6
6
|
require "net/ssh"
|
7
7
|
require "net/sftp"
|
8
|
+
require "more_ruby"
|
8
9
|
|
9
10
|
|
10
11
|
class XXabbrevupperxxHelper
|
11
12
|
include Test::Unit::Assertions
|
12
|
-
include FrameworkHelpers
|
13
13
|
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
case
|
22
|
-
when /^xxabbrevxx/
|
23
|
-
|
14
|
+
# E.g. calling homepage.displayed? from a test script :
|
15
|
+
# Test cannot see homepage so its call is routed through method_missing
|
16
|
+
# If method_missing returns an instance of the class, .displayed? can be called on it (seamlessly)
|
17
|
+
# At present this will happen for every call to a page from a test script
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
#puts "XXabbrevupperxxHelper method_missing called; name = #{name.inspect}; #{name.class}"
|
20
|
+
|
21
|
+
case name.to_s
|
22
|
+
when /^xxabbrevxx/i
|
23
|
+
RSPages.find(name.to_s) # return the page so that the helper can use it
|
24
24
|
else
|
25
25
|
super
|
26
26
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class XXabbrevupperxxHelper
|
3
|
+
|
4
|
+
def new_browser_at_url(url)
|
5
|
+
puts "New browser at #{url}"
|
6
|
+
case ZZnamezzConfig::BROWSER
|
7
|
+
when :chrome
|
8
|
+
# Set detach to true so the browser remains open once the test finishes
|
9
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
10
|
+
options.add_option(:detach, true)
|
11
|
+
b = Watir::Browser.new :chrome, :options => options
|
12
|
+
|
13
|
+
when :firefox
|
14
|
+
b = Watir::Browser.new :firefox
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
b.goto url
|
19
|
+
|
20
|
+
# Store the new browser in the global list
|
21
|
+
$browsers << b
|
22
|
+
|
23
|
+
b # return the browser
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
# All methods in this module will be added to those classes. If methods with the
|
3
|
+
# same names exist, these methods will overwrite the pre-existing ones.
|
4
|
+
|
5
|
+
module ZZnamezzCustom
|
6
|
+
|
7
|
+
# Example code :
|
8
|
+
#
|
9
|
+
# class XXabbrevupperxxPage
|
10
|
+
#
|
11
|
+
# # All page titles exist in the same-named element
|
12
|
+
# def title_field
|
13
|
+
# browser.h1(:id => "page_title")
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# def title
|
17
|
+
# title_field.value
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# class XXabbrevupperxxHomepage
|
23
|
+
#
|
24
|
+
# def displayed?
|
25
|
+
# puts "In #{__method__}"
|
26
|
+
# title == "Welcome to ZZnamezz"
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
|
32
|
+
end
|
@@ -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 xxabbrevxx_pages
|
110
|
+
# The linkage should be that the NAME of the xxabbrevxx_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 xxabbrevxx_pages (in this example, :id, "user_role" )
|
113
|
+
# Until this is done, flow_item.name needs to match the xxabbrevxx_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 xxabbrevxx_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 xxabbrevxx_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 XXabbrevupperxxFN
|
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 ZZnamezz, 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 'xxabbrevxx_flow_names'
|
9
|
+
|
10
|
+
class XXabbrevupperxxFlows
|
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(XXabbrevupperxxFN::GOTO_HOMEPAGE)
|
23
|
+
flow.add(FlowLink.new("goto_homepage", "xxabbrevxx_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(XXabbrevupperxxFN::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(XXabbrevupperxxFN::CREATE_USER)
|
33
|
+
add_existing_flow_to_flow(flow, XXabbrevupperxxFN::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, ZZnamezzConfig::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 "XXabbrevupperxxFlows 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, XXabbrevupperxxFN::GOTO_HOMEPAGE)
|
114
|
+
flow
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|