rutema 2.0.1 → 2.0.2
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 +4 -2
- data/README.md +12 -12
- data/bin/rutema +4 -4
- data/lib/rutema/application.rb +34 -31
- data/lib/rutema/core/configuration.rb +123 -117
- data/lib/rutema/core/engine.rb +101 -113
- data/lib/rutema/core/framework.rb +54 -54
- data/lib/rutema/core/objectmodel.rb +101 -90
- data/lib/rutema/core/parser.rb +8 -8
- data/lib/rutema/core/reporter.rb +78 -68
- data/lib/rutema/core/runner.rb +116 -97
- data/lib/rutema/elements/minimal.rb +29 -25
- data/lib/rutema/parsers/xml.rb +100 -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 +7 -7
data/lib/rutema/core/runner.rb
CHANGED
|
@@ -4,132 +4,151 @@ 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
|
|
|
21
23
|
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
|
|
24
|
+
@context["spec_name"] = spec.name
|
|
25
|
+
steps = []
|
|
26
|
+
run_status = :success
|
|
27
|
+
state = { "start_time" => Time.now, "sequence_id" => @number_of_runs, :test => spec.name }
|
|
28
|
+
message(:test => spec.name, :text => "started")
|
|
29
|
+
if @setup
|
|
30
|
+
message(:test => spec.name, :text => "setup")
|
|
31
|
+
run_status, steps = execute_and_collect_state("_setup_", @setup.scenario, true, run_status, steps)
|
|
32
|
+
end
|
|
33
|
+
if run_status == :error
|
|
34
|
+
message(:test => spec.name, "number" => 0, "status" => :error, "out" => "Setup failed", "err" => "", "duration" => 0)
|
|
35
|
+
else
|
|
36
|
+
message(:test => spec.name, :text => "running")
|
|
37
|
+
run_status, steps = execute_and_collect_state(spec.name, spec.scenario, is_special, run_status, steps)
|
|
71
38
|
end
|
|
39
|
+
@context["rutema_status"] = run_status
|
|
40
|
+
if @teardown
|
|
41
|
+
message(:test => spec.name, :text => "teardown")
|
|
42
|
+
run_status, steps = execute_and_collect_state("_teardown_", @teardown.scenario, true, run_status, steps)
|
|
43
|
+
end
|
|
44
|
+
wrap_up_execution(run_status, steps, spec, state)
|
|
45
|
+
ensure
|
|
46
|
+
ensure_cleanup_on_exception
|
|
72
47
|
end
|
|
73
48
|
|
|
74
49
|
private
|
|
75
50
|
|
|
51
|
+
def execute_and_collect_state(test_name, scenario, is_special, current_run_status, steps_until_now)
|
|
52
|
+
executed_steps, scenario_status = run_scenario(test_name, scenario, @context, is_special)
|
|
53
|
+
current_run_status = scenario_status unless STATUS_CODES.find_index(scenario_status) < STATUS_CODES.find_index(current_run_status)
|
|
54
|
+
steps_until_now += executed_steps
|
|
55
|
+
return current_run_status, steps_until_now
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def wrap_up_execution(run_status, executed_steps, last_run_spec, state)
|
|
59
|
+
@context["rutema_status"] = run_status
|
|
60
|
+
message(:test => last_run_spec.name, :text => "finished")
|
|
61
|
+
state["status"] = run_status
|
|
62
|
+
state["stop_time"] = Time.now
|
|
63
|
+
state["steps"] = executed_steps
|
|
64
|
+
@number_of_runs += 1
|
|
65
|
+
return state
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ensure_cleanup_on_exception
|
|
69
|
+
cleanup_exception = nil
|
|
70
|
+
@cleanup_blocks.each do |cleanup_block|
|
|
71
|
+
# Try all blocks
|
|
72
|
+
|
|
73
|
+
cleanup_block.run(@context) if cleanup_block.respond_to?(:run)
|
|
74
|
+
# rubocop:disable Lint/RescueException
|
|
75
|
+
rescue Exception => e
|
|
76
|
+
# Ignore errors, ensure all cleanup steps are attempted
|
|
77
|
+
cleanup_exception = e
|
|
78
|
+
end
|
|
79
|
+
# rubocop:enable Lint/RescueException
|
|
80
|
+
raise cleanup_exception unless cleanup_exception.nil?
|
|
81
|
+
ensure
|
|
82
|
+
@cleanup_blocks = []
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
76
86
|
def run_scenario(name, scenario, meta, is_special)
|
|
77
|
-
executed_steps=[]
|
|
78
|
-
status
|
|
79
|
-
begin
|
|
80
|
-
stps=scenario.steps
|
|
87
|
+
executed_steps = []
|
|
88
|
+
status = :skipped
|
|
89
|
+
begin
|
|
90
|
+
stps = scenario.steps
|
|
81
91
|
if stps.empty?
|
|
82
|
-
error(name,"Scenario #{name} contains no steps")
|
|
83
|
-
status
|
|
92
|
+
error(name, "Scenario #{name} contains no steps")
|
|
93
|
+
status = :error
|
|
84
94
|
else
|
|
85
95
|
stps.each do |s|
|
|
86
96
|
if status == :error && s.skip_on_error?
|
|
87
|
-
message(:test=>name
|
|
97
|
+
message(:test => name, :text => s.to_s, "number" => s.number, "status" => :skipped, "is_special" => is_special)
|
|
88
98
|
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?
|
|
99
|
+
executed_step = next_step(s, name, meta, is_special)
|
|
100
|
+
status = executed_step.status unless STATUS_CODES.find_index(executed_step.status) < STATUS_CODES.find_index(status)
|
|
101
|
+
executed_steps << executed_step
|
|
102
|
+
break if s.status == :error && !s.continue?
|
|
109
103
|
end
|
|
110
104
|
end
|
|
111
105
|
end
|
|
112
|
-
rescue
|
|
113
|
-
error(name
|
|
114
|
-
status
|
|
106
|
+
rescue StandardError
|
|
107
|
+
error(name, "#{$!.message}\n#{$!.backtrace.join("\n")}")
|
|
108
|
+
status = :error
|
|
115
109
|
end
|
|
116
|
-
return executed_steps,status
|
|
110
|
+
return executed_steps, status
|
|
117
111
|
end
|
|
112
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
118
113
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
def next_step(step_spec, test_name, meta, test_is_special)
|
|
115
|
+
message(
|
|
116
|
+
:test => test_name, :text => step_spec.to_s, "number" => step_spec.number,
|
|
117
|
+
"status" => :started, "is_special" => test_is_special
|
|
118
|
+
)
|
|
119
|
+
sleep 0.05
|
|
120
|
+
begin
|
|
121
|
+
cache_cleanup(step_spec)
|
|
122
|
+
executed_step = run_step(step_spec, meta)
|
|
123
|
+
# rubocop:disable Lint/RescueException
|
|
124
|
+
rescue Exception => e
|
|
125
|
+
throw e unless step_spec.continue?
|
|
126
|
+
step_spec.status = :error
|
|
127
|
+
# rubocop:enable Lint/RescueException
|
|
122
128
|
end
|
|
129
|
+
message(
|
|
130
|
+
:test => test_name, :text => step_spec.to_s, "number" => step_spec.number,
|
|
131
|
+
"status" => step_spec.status, "out" => step_spec.output, "err" => step_spec.error,
|
|
132
|
+
"backtrace" => step_spec.backtrace, "duration" => step_spec.exec_time,
|
|
133
|
+
"is_special" => test_is_special
|
|
134
|
+
)
|
|
135
|
+
return executed_step
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def cache_cleanup(step)
|
|
139
|
+
return unless step.has_cleanup? && step.cleanup.respond_to?(:run)
|
|
140
|
+
|
|
141
|
+
@cleanup_blocks << step.cleanup
|
|
123
142
|
end
|
|
124
143
|
|
|
125
|
-
def run_step
|
|
144
|
+
def run_step(step, meta)
|
|
126
145
|
if step.has_cmd? && step.cmd.respond_to?(:run)
|
|
127
146
|
step.cmd.run(meta)
|
|
128
147
|
else
|
|
129
148
|
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
|
130
|
-
step.status
|
|
149
|
+
step.status = :warning
|
|
131
150
|
end
|
|
132
|
-
step.status
|
|
151
|
+
step.status = :success if step.ignore?
|
|
133
152
|
return step
|
|
134
153
|
end
|
|
135
154
|
end
|
|
@@ -141,20 +160,20 @@ module Rutema
|
|
|
141
160
|
# Steps that do not respond to +:run+ have their status set to +:warning+.
|
|
142
161
|
#
|
|
143
162
|
# Returns the step after "executing" it successfully
|
|
144
|
-
class NoOp<Default
|
|
163
|
+
class NoOp < Default
|
|
145
164
|
##
|
|
146
165
|
# Simulate running the step by setting its status to +:success+
|
|
147
166
|
#
|
|
148
167
|
# If the step does not respond to +:run+ then +:warning+ is set as its
|
|
149
168
|
# status.
|
|
150
169
|
#
|
|
151
|
-
# * +step+ -
|
|
152
|
-
def run_step
|
|
170
|
+
# * +step+ -
|
|
171
|
+
def run_step(step, _meta)
|
|
153
172
|
unless step.has_cmd? && step.cmd.respond_to?(:run)
|
|
154
173
|
message("No command associated with step '#{step.step_type}'. Step number is #{step.number}")
|
|
155
|
-
step.status
|
|
174
|
+
step.status = :warning
|
|
156
175
|
end
|
|
157
|
-
step.status
|
|
176
|
+
step.status = :success if step.ignore?
|
|
158
177
|
return step
|
|
159
178
|
end
|
|
160
179
|
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,51 @@ 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
|
-
#Parses the 'scenario' XML element and returns the Rutema::Scenario instance
|
|
107
|
-
def parse_scenario
|
|
108
|
-
scenario=Rutema::Scenario.new([])
|
|
109
|
-
xmldoc=REXML::Document.new(
|
|
110
|
-
xmldoc.root.attributes.each do |attr,value|
|
|
111
|
-
add_attribute(scenario,attr,value)
|
|
109
|
+
# Parses the 'scenario' XML element and returns the Rutema::Scenario instance
|
|
110
|
+
def parse_scenario(xmltxt)
|
|
111
|
+
scenario = Rutema::Scenario.new([])
|
|
112
|
+
xmldoc = REXML::Document.new(xmltxt)
|
|
113
|
+
xmldoc.root.attributes.each do |attr, value|
|
|
114
|
+
add_attribute(scenario, attr, value)
|
|
112
115
|
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)
|
|
116
|
+
number = 0
|
|
117
|
+
xmldoc.root.elements.each do |el|
|
|
118
|
+
step = parse_step(el.to_s)
|
|
119
|
+
if step.step_type == "include_scenario"
|
|
120
|
+
included_scenario = include_scenario(step)
|
|
118
121
|
included_scenario.steps.each do |st|
|
|
119
|
-
number+=1
|
|
120
|
-
st.number=number
|
|
121
|
-
st.included_in=step.file
|
|
122
|
+
number += 1
|
|
123
|
+
st.number = number
|
|
124
|
+
st.included_in = step.file
|
|
122
125
|
scenario.add_step(st)
|
|
123
126
|
end
|
|
124
127
|
else
|
|
125
|
-
number+=1
|
|
126
|
-
step.number=number
|
|
128
|
+
number += 1
|
|
129
|
+
step.number = number
|
|
127
130
|
scenario.add_step(step)
|
|
128
131
|
end
|
|
129
132
|
end
|
|
130
133
|
return scenario
|
|
131
134
|
end
|
|
132
135
|
|
|
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
|
-
|
|
136
|
+
# Parses xml and returns the Rutema::Step instance
|
|
137
|
+
def parse_step(xmltxt)
|
|
138
|
+
xmldoc = REXML::Document.new(xmltxt)
|
|
139
|
+
# any step element
|
|
140
|
+
step = Rutema::Step.new
|
|
141
|
+
step.ignore = false
|
|
142
|
+
step.continue = false
|
|
143
|
+
xmldoc.root.attributes.each do |attr, value|
|
|
144
|
+
add_attribute(step, attr, value)
|
|
142
145
|
end
|
|
143
|
-
step.text=xmldoc.root.text.strip if xmldoc.root.text
|
|
144
|
-
step.step_type=xmldoc.root.name
|
|
146
|
+
step.text = xmldoc.root.text.strip if xmldoc.root.text
|
|
147
|
+
step.step_type = xmldoc.root.name
|
|
145
148
|
return step
|
|
146
149
|
end
|
|
147
150
|
|
|
@@ -153,13 +156,15 @@ module Rutema
|
|
|
153
156
|
# * +attr+ - the name of the attribute which shall either be created or
|
|
154
157
|
# whose current value will be overridden
|
|
155
158
|
# * +value+ - the value which shall be set for the attribute
|
|
156
|
-
def add_attribute
|
|
159
|
+
def add_attribute(element, attr, value)
|
|
157
160
|
# If the string is a textual representation of a boolean value ...
|
|
158
161
|
if boolean?(value)
|
|
162
|
+
# rubocop:disable Security/Eval
|
|
159
163
|
# ... convert it to a boolean value
|
|
160
|
-
|
|
164
|
+
element.attribute(attr, eval(value))
|
|
165
|
+
# rubocop:enable Security/Eval
|
|
161
166
|
else
|
|
162
|
-
element.attribute(attr,value)
|
|
167
|
+
element.attribute(attr, value)
|
|
163
168
|
end
|
|
164
169
|
end
|
|
165
170
|
|
|
@@ -171,38 +176,40 @@ module Rutema
|
|
|
171
176
|
#
|
|
172
177
|
# * +attribute_value+ - the entity which shall be checked if it's a string
|
|
173
178
|
# representing a boolean value
|
|
174
|
-
def boolean?
|
|
175
|
-
return true if
|
|
179
|
+
def boolean?(attribute_value)
|
|
180
|
+
return true if ["true", "false"].include?(attribute_value)
|
|
181
|
+
|
|
176
182
|
return false
|
|
177
183
|
end
|
|
178
184
|
|
|
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
|
-
|
|
185
|
+
# handles <include_scenario> elements, adding the steps to the current scenario
|
|
186
|
+
def include_scenario(step)
|
|
187
|
+
raise Rutema::ParserError, "missing required attribute file in #{step}" unless step.has_file?
|
|
188
|
+
raise Rutema::ParserError, "Cannot find #{File.expand_path(step.file)}" unless File.exist?(File.expand_path(step.file))
|
|
189
|
+
|
|
190
|
+
step.file = File.expand_path(step.file)
|
|
191
|
+
include_content = File.read(step.file)
|
|
185
192
|
return parse_scenario(include_content)
|
|
186
193
|
end
|
|
187
194
|
|
|
188
195
|
##
|
|
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 |
|
|
196
|
+
#
|
|
197
|
+
def extension_handling(spec)
|
|
198
|
+
# change into the directory the spec is in to handle relative paths correctly
|
|
199
|
+
Dir.chdir(File.dirname(File.expand_path(spec.filename))) do |_path|
|
|
193
200
|
spec.scenario.steps.each do |step|
|
|
194
|
-
#do we have a method to handle the element?
|
|
201
|
+
# do we have a method to handle the element?
|
|
195
202
|
if respond_to?(:"element_#{step.step_type}")
|
|
196
203
|
begin
|
|
197
|
-
|
|
198
|
-
rescue
|
|
199
|
-
raise ParserError, ($!.message
|
|
200
|
-
end
|
|
204
|
+
send(:"element_#{step.step_type}", step)
|
|
205
|
+
rescue StandardError
|
|
206
|
+
raise ParserError, ("#{$!.message}\n#{$@.join("\n")}")
|
|
207
|
+
end
|
|
201
208
|
elsif @configuration.parser["strict_mode"]
|
|
202
|
-
raise ParserError,"No command element associated with #{step.step_type}. Missing element_#{step.step_type}"
|
|
209
|
+
raise ParserError, "No command element associated with #{step.step_type}. Missing element_#{step.step_type}"
|
|
203
210
|
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
206
213
|
return spec
|
|
207
214
|
end
|
|
208
215
|
end
|