rutema 2.0.1 → 2.0.3
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.
- checksums.yaml +4 -4
- data/History.txt +7 -2
- data/README.md +12 -12
- data/bin/rutema +4 -4
- data/lib/rutema/application.rb +34 -31
- data/lib/rutema/core/configuration.rb +122 -117
- data/lib/rutema/core/engine.rb +102 -113
- data/lib/rutema/core/framework.rb +54 -54
- data/lib/rutema/core/objectmodel.rb +112 -95
- data/lib/rutema/core/parser.rb +9 -10
- data/lib/rutema/core/reporter.rb +76 -70
- data/lib/rutema/core/runner.rb +118 -97
- data/lib/rutema/elements/minimal.rb +29 -25
- data/lib/rutema/parsers/xml.rb +101 -93
- data/lib/rutema/reporters/json.rb +18 -20
- data/lib/rutema/reporters/junit.rb +88 -80
- data/lib/rutema/version.rb +1 -1
- metadata +13 -14
data/lib/rutema/core/runner.rb
CHANGED
|
@@ -4,132 +4,153 @@ require_relative "framework"
|
|
|
4
4
|
|
|
5
5
|
module Rutema
|
|
6
6
|
module Runners
|
|
7
|
+
# The default test runner
|
|
7
8
|
class Default
|
|
8
9
|
include Rutema::Messaging
|
|
9
10
|
|
|
10
11
|
attr_reader :context
|
|
11
|
-
attr_accessor :setup
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@
|
|
15
|
-
@
|
|
12
|
+
attr_accessor :setup, :teardown
|
|
13
|
+
|
|
14
|
+
def initialize(context, queue)
|
|
15
|
+
@setup = nil
|
|
16
|
+
@teardown = nil
|
|
17
|
+
@context = context || {}
|
|
16
18
|
@queue = queue
|
|
17
|
-
@number_of_runs=0
|
|
19
|
+
@number_of_runs = 0
|
|
18
20
|
@cleanup_blocks = []
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
24
|
+
# rubocop:disable-next Style/OptionalBooleanParameter
|
|
21
25
|
def run(spec, is_special = false)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
executed_steps,testspec_status = run_scenario(spec.name, spec.scenario, @context, is_special)
|
|
37
|
-
status=testspec_status unless STATUS_CODES.find_index(testspec_status) < STATUS_CODES.find_index(status)
|
|
38
|
-
steps+=executed_steps
|
|
39
|
-
else
|
|
40
|
-
message(:test=>spec.name,'number'=>0,'status'=>:error,'out'=>"Setup failed",'err'=>"",'duration'=>0)
|
|
41
|
-
end
|
|
42
|
-
@context['rutema_status']=status
|
|
43
|
-
if @teardown
|
|
44
|
-
message(:test=>spec.name,:text=>'teardown')
|
|
45
|
-
executed_steps,teardown_status = run_scenario("_teardown_", @teardown.scenario, @context, true)
|
|
46
|
-
status=teardown_status unless STATUS_CODES.find_index(teardown_status) < STATUS_CODES.find_index(status)
|
|
47
|
-
end
|
|
48
|
-
@context['rutema_status']=status
|
|
49
|
-
message(:test=>spec.name,:text=>'finished')
|
|
50
|
-
state['status']=status
|
|
51
|
-
state["stop_time"]=Time.now
|
|
52
|
-
state['steps']=steps
|
|
53
|
-
@number_of_runs+=1
|
|
54
|
-
return state
|
|
55
|
-
ensure
|
|
56
|
-
begin
|
|
57
|
-
cleanup_exception = nil
|
|
58
|
-
@cleanup_blocks.each do |cleanup_block|
|
|
59
|
-
# Try all blocks
|
|
60
|
-
begin
|
|
61
|
-
cleanup_block.run(@context) if cleanup_block.respond_to?(:run)
|
|
62
|
-
rescue Exception => e
|
|
63
|
-
# Ignore errors, ensure all cleanup steps are attempted
|
|
64
|
-
cleanup_exception = e
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
raise cleanup_exception if !cleanup_exception.nil?
|
|
68
|
-
ensure
|
|
69
|
-
@cleanup_blocks = []
|
|
70
|
-
end
|
|
26
|
+
@context["spec_name"] = spec.name
|
|
27
|
+
steps = []
|
|
28
|
+
run_status = :success
|
|
29
|
+
state = { "start_time" => Time.now, "sequence_id" => @number_of_runs, :test => spec.name }
|
|
30
|
+
message(:test => spec.name, :text => "started")
|
|
31
|
+
if @setup
|
|
32
|
+
message(:test => spec.name, :text => "setup")
|
|
33
|
+
run_status, steps = execute_and_collect_state("_setup_", @setup.scenario, true, run_status, steps)
|
|
34
|
+
end
|
|
35
|
+
if run_status == :error
|
|
36
|
+
message(:test => spec.name, "number" => 0, "status" => :error, "out" => "Setup failed", "err" => "", "duration" => 0)
|
|
37
|
+
else
|
|
38
|
+
message(:test => spec.name, :text => "running")
|
|
39
|
+
run_status, steps = execute_and_collect_state(spec.name, spec.scenario, is_special, run_status, steps)
|
|
71
40
|
end
|
|
41
|
+
@context["rutema_status"] = run_status
|
|
42
|
+
if @teardown
|
|
43
|
+
message(:test => spec.name, :text => "teardown")
|
|
44
|
+
run_status, steps = execute_and_collect_state("_teardown_", @teardown.scenario, true, run_status, steps)
|
|
45
|
+
end
|
|
46
|
+
wrap_up_execution(run_status, steps, spec, state)
|
|
47
|
+
ensure
|
|
48
|
+
ensure_cleanup_on_exception
|
|
72
49
|
end
|
|
73
50
|
|
|
74
51
|
private
|
|
75
52
|
|
|
53
|
+
def execute_and_collect_state(test_name, scenario, is_special, current_run_status, steps_until_now)
|
|
54
|
+
executed_steps, scenario_status = run_scenario(test_name, scenario, @context, is_special)
|
|
55
|
+
current_run_status = scenario_status unless STATUS_CODES.find_index(scenario_status) < STATUS_CODES.find_index(current_run_status)
|
|
56
|
+
steps_until_now += executed_steps
|
|
57
|
+
return current_run_status, steps_until_now
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def wrap_up_execution(run_status, executed_steps, last_run_spec, state)
|
|
61
|
+
@context["rutema_status"] = run_status
|
|
62
|
+
message(:test => last_run_spec.name, :text => "finished")
|
|
63
|
+
state["status"] = run_status
|
|
64
|
+
state["stop_time"] = Time.now
|
|
65
|
+
state["steps"] = executed_steps
|
|
66
|
+
@number_of_runs += 1
|
|
67
|
+
return state
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ensure_cleanup_on_exception
|
|
71
|
+
cleanup_exception = nil
|
|
72
|
+
@cleanup_blocks.each do |cleanup_block|
|
|
73
|
+
# Try all blocks
|
|
74
|
+
|
|
75
|
+
cleanup_block.run(@context) if cleanup_block.respond_to?(:run)
|
|
76
|
+
# rubocop:disable Lint/RescueException
|
|
77
|
+
rescue Exception => e
|
|
78
|
+
# Ignore errors, ensure all cleanup steps are attempted
|
|
79
|
+
cleanup_exception = e
|
|
80
|
+
end
|
|
81
|
+
# rubocop:enable Lint/RescueException
|
|
82
|
+
raise cleanup_exception unless cleanup_exception.nil?
|
|
83
|
+
ensure
|
|
84
|
+
@cleanup_blocks = []
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
88
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
89
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
76
90
|
def run_scenario(name, scenario, meta, is_special)
|
|
77
|
-
executed_steps=[]
|
|
78
|
-
status
|
|
79
|
-
begin
|
|
80
|
-
stps=scenario.steps
|
|
91
|
+
executed_steps = []
|
|
92
|
+
status = :skipped
|
|
93
|
+
begin
|
|
94
|
+
stps = scenario.steps
|
|
81
95
|
if stps.empty?
|
|
82
|
-
error(name,"Scenario #{name} contains no steps")
|
|
83
|
-
status
|
|
96
|
+
error(name, "Scenario #{name} contains no steps")
|
|
97
|
+
status = :error
|
|
84
98
|
else
|
|
85
99
|
stps.each do |s|
|
|
86
100
|
if status == :error && s.skip_on_error?
|
|
87
|
-
message(:test=>name
|
|
101
|
+
message(:test => name, :text => s.to_s, "number" => s.number, "status" => :skipped, "is_special" => is_special)
|
|
88
102
|
else
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
sleep 0.05
|
|
94
|
-
begin
|
|
95
|
-
cache_cleanup(s)
|
|
96
|
-
executed_steps << run_step(s, meta)
|
|
97
|
-
rescue Exception => e
|
|
98
|
-
throw e unless s.continue?
|
|
99
|
-
s.status = :error
|
|
100
|
-
end
|
|
101
|
-
message(
|
|
102
|
-
:test => name, :text => s.to_s, 'number' => s.number,
|
|
103
|
-
'status' => s.status, 'out' => s.output, 'err' => s.error,
|
|
104
|
-
'backtrace' => s.backtrace, 'duration' => s.exec_time,
|
|
105
|
-
'is_special' => is_special
|
|
106
|
-
)
|
|
107
|
-
status=s.status unless STATUS_CODES.find_index(s.status) < STATUS_CODES.find_index(status)
|
|
108
|
-
break if :error == s.status and !s.continue?
|
|
103
|
+
executed_step = next_step(s, name, meta, is_special)
|
|
104
|
+
status = executed_step.status unless STATUS_CODES.find_index(executed_step.status) < STATUS_CODES.find_index(status)
|
|
105
|
+
executed_steps << executed_step
|
|
106
|
+
break if s.status == :error && !s.continue?
|
|
109
107
|
end
|
|
110
108
|
end
|
|
111
109
|
end
|
|
112
|
-
rescue
|
|
113
|
-
error(name
|
|
114
|
-
status
|
|
110
|
+
rescue StandardError
|
|
111
|
+
error(name, "#{$!.message}\n#{$!.backtrace.join("\n")}")
|
|
112
|
+
status = :error
|
|
115
113
|
end
|
|
116
|
-
return executed_steps,status
|
|
114
|
+
return executed_steps, status
|
|
117
115
|
end
|
|
118
116
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
def next_step(step_spec, test_name, meta, test_is_special)
|
|
118
|
+
message(
|
|
119
|
+
:test => test_name, :text => step_spec.to_s, "number" => step_spec.number,
|
|
120
|
+
"status" => :started, "is_special" => test_is_special
|
|
121
|
+
)
|
|
122
|
+
sleep 0.05
|
|
123
|
+
begin
|
|
124
|
+
cache_cleanup(step_spec)
|
|
125
|
+
executed_step = run_step(step_spec, meta)
|
|
126
|
+
# rubocop:disable-next Lint/RescueException
|
|
127
|
+
rescue Exception => e
|
|
128
|
+
throw e unless step_spec.continue?
|
|
129
|
+
step_spec.status = :error
|
|
122
130
|
end
|
|
131
|
+
message(
|
|
132
|
+
:test => test_name, :text => step_spec.to_s, "number" => step_spec.number,
|
|
133
|
+
"status" => step_spec.status, "out" => step_spec.output, "err" => step_spec.error,
|
|
134
|
+
"backtrace" => step_spec.backtrace, "duration" => step_spec.exec_time,
|
|
135
|
+
"is_special" => test_is_special
|
|
136
|
+
)
|
|
137
|
+
return executed_step
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def cache_cleanup(step)
|
|
141
|
+
return unless step.has_cleanup? && step.cleanup.respond_to?(:run)
|
|
142
|
+
|
|
143
|
+
@cleanup_blocks << step.cleanup
|
|
123
144
|
end
|
|
124
145
|
|
|
125
|
-
def run_step
|
|
146
|
+
def run_step(step, meta)
|
|
126
147
|
if step.has_cmd? && step.cmd.respond_to?(:run)
|
|
127
148
|
step.cmd.run(meta)
|
|
128
149
|
else
|
|
129
150
|
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
|
130
|
-
step.status
|
|
151
|
+
step.status = :warning
|
|
131
152
|
end
|
|
132
|
-
step.status
|
|
153
|
+
step.status = :success if step.ignore?
|
|
133
154
|
return step
|
|
134
155
|
end
|
|
135
156
|
end
|
|
@@ -141,20 +162,20 @@ module Rutema
|
|
|
141
162
|
# Steps that do not respond to +:run+ have their status set to +:warning+.
|
|
142
163
|
#
|
|
143
164
|
# Returns the step after "executing" it successfully
|
|
144
|
-
class NoOp<Default
|
|
165
|
+
class NoOp < Default
|
|
145
166
|
##
|
|
146
167
|
# Simulate running the step by setting its status to +:success+
|
|
147
168
|
#
|
|
148
169
|
# If the step does not respond to +:run+ then +:warning+ is set as its
|
|
149
170
|
# status.
|
|
150
171
|
#
|
|
151
|
-
# * +step+ -
|
|
152
|
-
def run_step
|
|
172
|
+
# * +step+ -
|
|
173
|
+
def run_step(step, _meta)
|
|
153
174
|
unless step.has_cmd? && step.cmd.respond_to?(:run)
|
|
154
175
|
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
|
155
|
-
step.status
|
|
176
|
+
step.status = :warning
|
|
156
177
|
end
|
|
157
|
-
step.status
|
|
178
|
+
step.status = :success if step.ignore?
|
|
158
179
|
return step
|
|
159
180
|
end
|
|
160
181
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "highline"
|
|
4
4
|
|
|
5
5
|
module Rutema
|
|
6
6
|
##
|
|
@@ -8,43 +8,47 @@ module Rutema
|
|
|
8
8
|
# functionality which can then be utilized in test specifications
|
|
9
9
|
module Elements
|
|
10
10
|
##
|
|
11
|
-
# Module offering an
|
|
11
|
+
# Module offering an example of a minimal set of elements for use as steps in
|
|
12
12
|
# test specifications
|
|
13
13
|
module Minimal
|
|
14
|
-
#echo prints a message on the screen:
|
|
14
|
+
# echo prints a message on the screen:
|
|
15
15
|
# <echo text="A meaningful message"/>
|
|
16
16
|
# <echo>A meaningful message</echo>
|
|
17
|
-
def element_echo
|
|
18
|
-
step.cmd=Patir::RubyCommand.new("echo")
|
|
17
|
+
def element_echo(step)
|
|
18
|
+
step.cmd = Patir::RubyCommand.new("echo") do |cmd|
|
|
19
|
+
cmd.error = ""
|
|
20
|
+
cmd.output = step.text.to_s
|
|
21
|
+
$stdout.puts(cmd.output)
|
|
22
|
+
:success
|
|
23
|
+
end
|
|
19
24
|
return step
|
|
20
25
|
end
|
|
21
26
|
|
|
22
|
-
#prompt asks the user a yes/no question. Answering yes means the step is
|
|
27
|
+
# prompt asks the user a yes/no question. Answering yes means the step is successful.
|
|
23
28
|
# <prompt text="Do you want fries with that?"/>
|
|
24
29
|
#
|
|
25
|
-
#A prompt element automatically makes a specification "attended"
|
|
26
|
-
def element_prompt
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
cmd.output=""
|
|
30
|
-
cmd.error=""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end#if
|
|
36
|
-
end#do rubycommand
|
|
30
|
+
# A prompt element automatically makes a specification "attended"
|
|
31
|
+
def element_prompt(step)
|
|
32
|
+
step.attended = true
|
|
33
|
+
step.cmd = Patir::RubyCommand.new("prompt") do |cmd|
|
|
34
|
+
cmd.output = ""
|
|
35
|
+
cmd.error = ""
|
|
36
|
+
raise "n" unless HighLine.new.agree(step.text.to_s)
|
|
37
|
+
|
|
38
|
+
step.output = "y"
|
|
39
|
+
end
|
|
37
40
|
return step
|
|
38
41
|
end
|
|
39
42
|
|
|
40
|
-
#command executes a shell command
|
|
43
|
+
# command executes a shell command
|
|
41
44
|
# <command cmd="useful_command.exe with parameters", working_directory="some/directory"/>
|
|
42
|
-
def element_command
|
|
43
|
-
raise ParserError,"missing required attribute cmd in #{step}" unless step.has_cmd?
|
|
44
|
-
|
|
45
|
-
wd=
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
def element_command(step)
|
|
46
|
+
raise ParserError, "missing required attribute cmd in #{step}" unless step.has_cmd?
|
|
47
|
+
|
|
48
|
+
wd = Dir.pwd
|
|
49
|
+
wd = step.working_directory if step.has_working_directory?
|
|
50
|
+
step.cmd = Patir::ShellCommand.new(:cmd => step.cmd, :working_directory => File.expand_path(wd))
|
|
51
|
+
return step
|
|
48
52
|
end
|
|
49
53
|
end
|
|
50
54
|
end
|
data/lib/rutema/parsers/xml.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Copyright (c) 2007-2021 Vassilis Rizopoulos. All rights reserved.
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
3
|
+
require "rexml/document"
|
|
4
|
+
require "patir/command"
|
|
5
|
+
require_relative "../core/parser"
|
|
6
|
+
require_relative "../core/objectmodel"
|
|
7
|
+
require_relative "../elements/minimal"
|
|
8
8
|
|
|
9
9
|
module Rutema
|
|
10
10
|
module Parsers
|
|
@@ -16,17 +16,17 @@ module Rutema
|
|
|
16
16
|
# +element_foo+.
|
|
17
17
|
#
|
|
18
18
|
# The method will receive a Rutema::Step instance as a parameter which it should return
|
|
19
|
-
class XML<SpecificationParser
|
|
19
|
+
class XML < SpecificationParser
|
|
20
20
|
include Rutema::Elements::Minimal
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
ELEM_SPEC="specification"
|
|
24
|
-
|
|
25
|
-
ELEM_DESC="specification/description"
|
|
26
|
-
|
|
27
|
-
ELEM_TITLE="specification/title"
|
|
28
|
-
|
|
29
|
-
ELEM_SCENARIO="specification/scenario"
|
|
22
|
+
# :nodoc:
|
|
23
|
+
ELEM_SPEC = "specification".freeze
|
|
24
|
+
# :nodoc:
|
|
25
|
+
ELEM_DESC = "specification/description".freeze
|
|
26
|
+
# :nodoc:
|
|
27
|
+
ELEM_TITLE = "specification/title".freeze
|
|
28
|
+
# :nodoc:
|
|
29
|
+
ELEM_SCENARIO = "specification/scenario".freeze
|
|
30
30
|
|
|
31
31
|
##
|
|
32
32
|
# Pass the given test specification and return a corresponding
|
|
@@ -36,28 +36,30 @@ module Rutema
|
|
|
36
36
|
# or the test specification itself.
|
|
37
37
|
#
|
|
38
38
|
# This will raise ParserError if an error occurs during parsing.
|
|
39
|
-
def parse_specification
|
|
40
|
-
@parsed||=[]
|
|
39
|
+
def parse_specification(param)
|
|
40
|
+
@parsed ||= []
|
|
41
41
|
begin
|
|
42
42
|
if File.exist?(param)
|
|
43
|
-
txt=File.read(param)
|
|
44
|
-
filename=File.expand_path(param)
|
|
43
|
+
txt = File.read(param)
|
|
44
|
+
filename = File.expand_path(param)
|
|
45
45
|
else
|
|
46
|
-
txt=param
|
|
47
|
-
filename=Dir.pwd
|
|
46
|
+
txt = param
|
|
47
|
+
filename = Dir.pwd
|
|
48
48
|
end
|
|
49
|
-
spec=parse_case(txt,filename)
|
|
50
|
-
raise Rutema::ParserError,"Missing required attribute 'name' in specification element" unless spec.has_name? && !spec.name.empty?
|
|
51
|
-
raise Rutema::ParserError,"Duplicate test name '#{spec.name}' in #{filename}" if @parsed.include?(spec.name)
|
|
52
|
-
|
|
49
|
+
spec = parse_case(txt, filename)
|
|
50
|
+
raise Rutema::ParserError, "Missing required attribute 'name' in specification element" unless spec.has_name? && !spec.name.empty?
|
|
51
|
+
raise Rutema::ParserError, "Duplicate test name '#{spec.name}' in #{filename}" if @parsed.include?(spec.name)
|
|
52
|
+
|
|
53
|
+
@parsed << spec.name
|
|
53
54
|
extension_handling(spec)
|
|
54
55
|
rescue REXML::ParseException
|
|
55
|
-
raise Rutema::ParserError
|
|
56
|
+
raise Rutema::ParserError, $!.message
|
|
56
57
|
end
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
private
|
|
60
61
|
|
|
62
|
+
# rubocop:disable Metrics/AbcSize
|
|
61
63
|
##
|
|
62
64
|
# Parse the XML specification of a testcase and create a corresponding
|
|
63
65
|
# Rutema::Specification instance
|
|
@@ -65,30 +67,31 @@ module Rutema
|
|
|
65
67
|
# * +xmltext+ - the actual test specification text which must be an XML
|
|
66
68
|
# document
|
|
67
69
|
# * +filename+ - the filename of the test specification file or the
|
|
68
|
-
# current working directory of the test execution (this
|
|
69
|
-
def parse_case
|
|
70
|
-
spec=Rutema::Specification.new({})
|
|
71
|
-
xmldoc=REXML::Document.new(
|
|
70
|
+
# current working directory of the test execution (this
|
|
71
|
+
def parse_case(xmltxt, filename)
|
|
72
|
+
spec = Rutema::Specification.new({})
|
|
73
|
+
xmldoc = REXML::Document.new(xmltxt)
|
|
72
74
|
validate_case(xmldoc)
|
|
73
|
-
xmldoc.root.attributes.each do |attr,value|
|
|
74
|
-
add_attribute(spec,attr,value)
|
|
75
|
+
xmldoc.root.attributes.each do |attr, value|
|
|
76
|
+
add_attribute(spec, attr, value)
|
|
75
77
|
end
|
|
76
|
-
spec.title=xmldoc.elements[ELEM_TITLE].text
|
|
77
|
-
spec.title||=""
|
|
78
|
+
spec.title = xmldoc.elements[ELEM_TITLE].text
|
|
79
|
+
spec.title ||= ""
|
|
78
80
|
spec.title.strip!
|
|
79
|
-
spec.description=xmldoc.elements[ELEM_DESC].text
|
|
80
|
-
spec.description||=""
|
|
81
|
+
spec.description = xmldoc.elements[ELEM_DESC].text
|
|
82
|
+
spec.description ||= ""
|
|
81
83
|
unless spec.description.empty?
|
|
82
84
|
spec.description.strip!
|
|
83
|
-
spec.description.gsub!(
|
|
84
|
-
end
|
|
85
|
+
spec.description.gsub!("\t", "")
|
|
86
|
+
end
|
|
85
87
|
Dir.chdir(File.dirname(filename)) do
|
|
86
|
-
spec.scenario=parse_scenario(xmldoc.elements[ELEM_SCENARIO].to_s) if xmldoc.elements[ELEM_SCENARIO]
|
|
88
|
+
spec.scenario = parse_scenario(xmldoc.elements[ELEM_SCENARIO].to_s) if xmldoc.elements[ELEM_SCENARIO]
|
|
87
89
|
end
|
|
88
|
-
spec.filename=filename
|
|
90
|
+
spec.filename = filename
|
|
89
91
|
return spec
|
|
90
92
|
end
|
|
91
93
|
|
|
94
|
+
# rubocop:enable Metrics/AbcSize
|
|
92
95
|
##
|
|
93
96
|
# Conduct a simple validation of the XML document by checking if it
|
|
94
97
|
# contains all necessary elements
|
|
@@ -97,51 +100,52 @@ module Rutema
|
|
|
97
100
|
#
|
|
98
101
|
# * +xmldoc+ - the text of the XML document to be checked for the
|
|
99
102
|
# necessary elements
|
|
100
|
-
def validate_case
|
|
101
|
-
raise Rutema::ParserError,"missing #{ELEM_SPEC} element in #{xmldoc}" unless xmldoc.elements[ELEM_SPEC]
|
|
102
|
-
raise Rutema::ParserError,"missing #{ELEM_DESC} element in #{xmldoc}" unless xmldoc.elements[ELEM_DESC]
|
|
103
|
-
raise Rutema::ParserError,"missing #{ELEM_TITLE} element in #{xmldoc}" unless xmldoc.elements[ELEM_TITLE]
|
|
103
|
+
def validate_case(xmldoc)
|
|
104
|
+
raise Rutema::ParserError, "missing #{ELEM_SPEC} element in #{xmldoc}" unless xmldoc.elements[ELEM_SPEC]
|
|
105
|
+
raise Rutema::ParserError, "missing #{ELEM_DESC} element in #{xmldoc}" unless xmldoc.elements[ELEM_DESC]
|
|
106
|
+
raise Rutema::ParserError, "missing #{ELEM_TITLE} element in #{xmldoc}" unless xmldoc.elements[ELEM_TITLE]
|
|
104
107
|
end
|
|
105
108
|
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
xmldoc
|
|
111
|
-
|
|
109
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
110
|
+
# Parses the 'scenario' XML element and returns the Rutema::Scenario instance
|
|
111
|
+
def parse_scenario(xmltxt)
|
|
112
|
+
scenario = Rutema::Scenario.new([])
|
|
113
|
+
xmldoc = REXML::Document.new(xmltxt)
|
|
114
|
+
xmldoc.root.attributes.each do |attr, value|
|
|
115
|
+
add_attribute(scenario, attr, value)
|
|
112
116
|
end
|
|
113
|
-
number=0
|
|
114
|
-
xmldoc.root.elements.each do |el|
|
|
115
|
-
step=parse_step(el.to_s)
|
|
116
|
-
if step.step_type=="include_scenario"
|
|
117
|
-
included_scenario=include_scenario(step)
|
|
117
|
+
number = 0
|
|
118
|
+
xmldoc.root.elements.each do |el|
|
|
119
|
+
step = parse_step(el.to_s)
|
|
120
|
+
if step.step_type == "include_scenario"
|
|
121
|
+
included_scenario = include_scenario(step)
|
|
118
122
|
included_scenario.steps.each do |st|
|
|
119
|
-
number+=1
|
|
120
|
-
st.number=number
|
|
121
|
-
st.included_in=step.file
|
|
123
|
+
number += 1
|
|
124
|
+
st.number = number
|
|
125
|
+
st.included_in = step.file
|
|
122
126
|
scenario.add_step(st)
|
|
123
127
|
end
|
|
124
128
|
else
|
|
125
|
-
number+=1
|
|
126
|
-
step.number=number
|
|
129
|
+
number += 1
|
|
130
|
+
step.number = number
|
|
127
131
|
scenario.add_step(step)
|
|
128
132
|
end
|
|
129
133
|
end
|
|
130
134
|
return scenario
|
|
131
135
|
end
|
|
132
136
|
|
|
133
|
-
#Parses xml and returns the Rutema::Step instance
|
|
134
|
-
def parse_step
|
|
135
|
-
xmldoc=REXML::Document.new(
|
|
136
|
-
#any step element
|
|
137
|
-
step=Rutema::Step.new
|
|
138
|
-
step.ignore=false
|
|
139
|
-
step.continue=false
|
|
140
|
-
xmldoc.root.attributes.each do |attr,value|
|
|
141
|
-
|
|
137
|
+
# Parses xml and returns the Rutema::Step instance
|
|
138
|
+
def parse_step(xmltxt)
|
|
139
|
+
xmldoc = REXML::Document.new(xmltxt)
|
|
140
|
+
# any step element
|
|
141
|
+
step = Rutema::Step.new
|
|
142
|
+
step.ignore = false
|
|
143
|
+
step.continue = false
|
|
144
|
+
xmldoc.root.attributes.each do |attr, value|
|
|
145
|
+
add_attribute(step, attr, value)
|
|
142
146
|
end
|
|
143
|
-
step.text=xmldoc.root.text.strip if xmldoc.root.text
|
|
144
|
-
step.step_type=xmldoc.root.name
|
|
147
|
+
step.text = xmldoc.root.text.strip if xmldoc.root.text
|
|
148
|
+
step.step_type = xmldoc.root.name
|
|
145
149
|
return step
|
|
146
150
|
end
|
|
147
151
|
|
|
@@ -153,13 +157,15 @@ module Rutema
|
|
|
153
157
|
# * +attr+ - the name of the attribute which shall either be created or
|
|
154
158
|
# whose current value will be overridden
|
|
155
159
|
# * +value+ - the value which shall be set for the attribute
|
|
156
|
-
def add_attribute
|
|
160
|
+
def add_attribute(element, attr, value)
|
|
157
161
|
# If the string is a textual representation of a boolean value ...
|
|
158
162
|
if boolean?(value)
|
|
163
|
+
# rubocop:disable Security/Eval
|
|
159
164
|
# ... convert it to a boolean value
|
|
160
|
-
|
|
165
|
+
element.attribute(attr, eval(value))
|
|
166
|
+
# rubocop:enable Security/Eval
|
|
161
167
|
else
|
|
162
|
-
element.attribute(attr,value)
|
|
168
|
+
element.attribute(attr, value)
|
|
163
169
|
end
|
|
164
170
|
end
|
|
165
171
|
|
|
@@ -171,38 +177,40 @@ module Rutema
|
|
|
171
177
|
#
|
|
172
178
|
# * +attribute_value+ - the entity which shall be checked if it's a string
|
|
173
179
|
# representing a boolean value
|
|
174
|
-
def boolean?
|
|
175
|
-
return true if
|
|
180
|
+
def boolean?(attribute_value)
|
|
181
|
+
return true if ["true", "false"].include?(attribute_value)
|
|
182
|
+
|
|
176
183
|
return false
|
|
177
184
|
end
|
|
178
185
|
|
|
179
|
-
#handles <include_scenario> elements, adding the steps to the current scenario
|
|
180
|
-
def include_scenario
|
|
181
|
-
raise Rutema::ParserError,"missing required attribute file in #{step}" unless step.has_file?
|
|
182
|
-
raise Rutema::ParserError,"Cannot find #{File.expand_path(step.file)}" unless File.exist?(File.expand_path(step.file))
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
# handles <include_scenario> elements, adding the steps to the current scenario
|
|
187
|
+
def include_scenario(step)
|
|
188
|
+
raise Rutema::ParserError, "missing required attribute file in #{step}" unless step.has_file?
|
|
189
|
+
raise Rutema::ParserError, "Cannot find #{File.expand_path(step.file)}" unless File.exist?(File.expand_path(step.file))
|
|
190
|
+
|
|
191
|
+
step.file = File.expand_path(step.file)
|
|
192
|
+
include_content = File.read(step.file)
|
|
185
193
|
return parse_scenario(include_content)
|
|
186
194
|
end
|
|
187
195
|
|
|
188
196
|
##
|
|
189
|
-
#
|
|
190
|
-
def extension_handling
|
|
191
|
-
#change into the directory the spec is in to handle relative paths correctly
|
|
192
|
-
Dir.chdir(File.dirname(File.expand_path(spec.filename))) do |
|
|
197
|
+
#
|
|
198
|
+
def extension_handling(spec)
|
|
199
|
+
# change into the directory the spec is in to handle relative paths correctly
|
|
200
|
+
Dir.chdir(File.dirname(File.expand_path(spec.filename))) do |_path|
|
|
193
201
|
spec.scenario.steps.each do |step|
|
|
194
|
-
#do we have a method to handle the element?
|
|
202
|
+
# do we have a method to handle the element?
|
|
195
203
|
if respond_to?(:"element_#{step.step_type}")
|
|
196
204
|
begin
|
|
197
|
-
|
|
198
|
-
rescue
|
|
199
|
-
raise ParserError, ($!.message
|
|
200
|
-
end
|
|
205
|
+
send(:"element_#{step.step_type}", step)
|
|
206
|
+
rescue StandardError
|
|
207
|
+
raise ParserError, ("#{$!.message}\n#{$@.join("\n")}")
|
|
208
|
+
end
|
|
201
209
|
elsif @configuration.parser["strict_mode"]
|
|
202
|
-
raise ParserError,"No command element associated with #{step.step_type}. Missing element_#{step.step_type}"
|
|
210
|
+
raise ParserError, "No command element associated with #{step.step_type}. Missing element_#{step.step_type}"
|
|
203
211
|
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
206
214
|
return spec
|
|
207
215
|
end
|
|
208
216
|
end
|