cuporter 0.3.10 → 0.3.12
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +86 -70
- data/Rakefile +6 -5
- data/bin/cuporter +2 -0
- data/config/batch_mode.example.yml +47 -0
- data/config/cli/options.rb +19 -3
- data/config/configuration.rb +23 -126
- data/config/cuporter.example.yml +8 -0
- data/config/option_set.rb +79 -0
- data/config/yaml_file/option_set_collection.rb +42 -0
- data/lib/cuporter.rb +1 -3
- data/lib/cuporter/feature_parser.rb +6 -70
- data/lib/cuporter/feature_parser/language.rb +41 -0
- data/lib/cuporter/feature_parser/node_parser.rb +64 -0
- data/lib/cuporter/feature_parser/old_gherkin_yaml/i18n.rb +9 -0
- data/lib/cuporter/feature_parser/parser_base.rb +84 -0
- data/lib/cuporter/feature_parser/tag_nodes_parser.rb +96 -0
- data/lib/cuporter/logging.rb +32 -0
- data/lib/cuporter/node.rb +6 -6
- data/lib/cuporter/report/tag_report.rb +1 -1
- metadata +31 -9
- data/lib/cuporter/node_parser.rb +0 -62
- data/lib/cuporter/tag_nodes_parser.rb +0 -94
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Config
|
4
|
+
|
5
|
+
class OptionSet
|
6
|
+
|
7
|
+
attr_reader :options
|
8
|
+
|
9
|
+
def initialize(file_config = {})
|
10
|
+
# CLI options replace any found in the file
|
11
|
+
cli_options_over_file_options = file_config.merge(Cuporter::Config::CLI::Options.options)
|
12
|
+
|
13
|
+
# defaults will be used for anything not so far specified in the file
|
14
|
+
# or CLI
|
15
|
+
@options = post_process(Cuporter::Config::DEFAULTS.merge(cli_options_over_file_options))
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
@options[key]
|
20
|
+
end
|
21
|
+
|
22
|
+
def output_file(format)
|
23
|
+
if (file = @options[:output_file].find {|f| f =~ ext_for(format) })
|
24
|
+
File.open(file, "w")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def dump
|
29
|
+
col_1_max = @options.keys.max_by {|i| i.to_s.length }.to_s.size
|
30
|
+
@options.each do |key, value|
|
31
|
+
puts ":#{key.to_s.ljust(col_1_max, ' ')} => #{value.inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def dry_run?
|
36
|
+
@options[:dry_run]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def post_process(options)
|
42
|
+
options[:input_file_pattern] = options.delete(:input_file) || "#{options.delete(:input_dir)}/**/*.feature"
|
43
|
+
options[:root_dir] = options[:input_file_pattern].split(File::SEPARATOR).first
|
44
|
+
options[:filter_args] = Cuporter::Config::CLI::FilterArgsBuilder.new(options.delete(:tags)).args
|
45
|
+
options[:output_file].each_with_index do |file_path, i|
|
46
|
+
options[:output_file][i] = full_path(options[:output_home], file_path.dup )
|
47
|
+
end
|
48
|
+
|
49
|
+
unless options[:output_file].find {|f| f =~ /\.html$/ }
|
50
|
+
options[:copy_public_assets] = options[:use_copied_public_assets] = false
|
51
|
+
end
|
52
|
+
|
53
|
+
unless options[:output_file].empty?
|
54
|
+
options[:log_dir] = File.dirname(options[:output_file].first)
|
55
|
+
end
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
def full_path(root_dir, file_path)
|
60
|
+
path = root_dir.empty? ? file_path : File.join(root_dir, file_path)
|
61
|
+
expanded_path = File.expand_path(path)
|
62
|
+
path_nodes = expanded_path.split(File::SEPARATOR)
|
63
|
+
file = path_nodes.pop
|
64
|
+
FileUtils.makedirs(path_nodes.join(File::SEPARATOR))
|
65
|
+
expanded_path
|
66
|
+
end
|
67
|
+
|
68
|
+
def ext_for(format)
|
69
|
+
case format
|
70
|
+
when 'text', 'pretty'
|
71
|
+
/\.txt$/
|
72
|
+
else
|
73
|
+
/\.#{format}$/
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
module Cuporter
|
3
|
+
module Config
|
4
|
+
module YamlFile
|
5
|
+
class OptionSetCollection
|
6
|
+
|
7
|
+
def self.path
|
8
|
+
Cuporter::Config::CLI::Options[:config_file] || "config/cuporter.yml"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.config_file
|
12
|
+
return [] unless File.exists?(path)
|
13
|
+
|
14
|
+
require 'yaml'
|
15
|
+
yaml = YAML.load_file(path)
|
16
|
+
if yaml["option_sets"]
|
17
|
+
yaml["option_sets"].map do |option_set|
|
18
|
+
cast(option_set["options"])
|
19
|
+
end
|
20
|
+
else
|
21
|
+
yaml["defaults"].empty? ? [] : [cast(yaml["defaults"])]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.cast(option_set)
|
26
|
+
pairs = {}
|
27
|
+
option_set.each do |key, value|
|
28
|
+
pairs[key.to_sym] = case key
|
29
|
+
when /^(tags|output_file|format)$/i
|
30
|
+
value.is_a?(Array) ? value : [value]
|
31
|
+
else
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
return pairs
|
37
|
+
end
|
38
|
+
|
39
|
+
end # class
|
40
|
+
end # File
|
41
|
+
end # Config
|
42
|
+
end
|
data/lib/cuporter.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
2
|
$LOAD_PATH.unshift( File.expand_path("#{File.dirname(__FILE__)}"))
|
3
|
-
$LOAD_PATH.unshift( File.expand_path("#{File.dirname(__FILE__)}/.."))
|
4
3
|
require 'cuporter/extensions/string'
|
4
|
+
require 'cuporter/logging'
|
5
5
|
require 'cuporter/node'
|
6
6
|
require 'cuporter/formatters/text'
|
7
7
|
require 'cuporter/formatters/csv'
|
8
8
|
require 'cuporter/filter'
|
9
9
|
require 'cuporter/feature_parser'
|
10
|
-
require 'cuporter/tag_nodes_parser'
|
11
|
-
require 'cuporter/node_parser'
|
12
10
|
require 'cuporter/document'
|
13
11
|
require 'cuporter/document/assets'
|
14
12
|
require 'cuporter/document/html_document'
|
@@ -1,16 +1,11 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
require 'cuporter/feature_parser/language'
|
3
|
+
require 'cuporter/feature_parser/parser_base'
|
4
|
+
require 'cuporter/feature_parser/tag_nodes_parser'
|
5
|
+
require 'cuporter/feature_parser/node_parser'
|
2
6
|
|
3
7
|
module Cuporter
|
4
|
-
|
5
|
-
FEATURE_LINE = /^\s*(Feature:[^#]*)/u
|
6
|
-
TAG_LINE = /^\s*(@\w.+)/u
|
7
|
-
SCENARIO_LINE = /^\s*(Scenario:[^#]*)$/u
|
8
|
-
SCENARIO_OUTLINE_LINE = /^\s*(Scenario Outline:[^#]*)$/u
|
9
|
-
SCENARIO_SET_LINE = /^\s*(Scenarios:[^#]*)$/u
|
10
|
-
EXAMPLE_SET_LINE = /^\s*(Examples:[^#]*)$/u
|
11
|
-
EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/u
|
12
|
-
PY_STRING_LINE = /^\s*"""\s*$/u
|
13
|
-
|
8
|
+
module FeatureParser
|
14
9
|
# adds a node to the doc for each cucumber '@' tag, populated with features and
|
15
10
|
# scenarios
|
16
11
|
def self.tag_nodes(file, report, filter, root_dir)
|
@@ -25,64 +20,5 @@ module Cuporter
|
|
25
20
|
parser.root = root_dir
|
26
21
|
parser.parse_feature
|
27
22
|
end
|
28
|
-
attr_writer :root
|
29
|
-
|
30
|
-
def initialize(file)
|
31
|
-
@file = file
|
32
|
-
@current_tags = []
|
33
|
-
@lines = File.read(@file).split(/\n/)
|
34
|
-
end
|
35
|
-
|
36
|
-
def file_relative_path
|
37
|
-
@file_relative_path ||= @file.sub(/^.*#{@root}\//,"#{@root}/")
|
38
|
-
end
|
39
|
-
|
40
|
-
def parse_feature
|
41
|
-
@open_comment_block = false
|
42
|
-
|
43
|
-
@lines.each do |line|
|
44
|
-
next if @open_comment_block && line !~ PY_STRING_LINE
|
45
|
-
|
46
|
-
case line
|
47
|
-
when PY_STRING_LINE
|
48
|
-
# toggle, to declare the multiline comment 'heredoc' open or closed
|
49
|
-
@open_comment_block = !@open_comment_block
|
50
|
-
when TAG_LINE
|
51
|
-
# may be more than one tag line
|
52
|
-
@current_tags |= clean_cuke_line($1).split(/\s+/)
|
53
|
-
when FEATURE_LINE
|
54
|
-
@feature = new_feature_node(clean_cuke_line($1), file_relative_path)
|
55
|
-
@current_tags = []
|
56
|
-
when SCENARIO_LINE
|
57
|
-
# How do we know when we have read all the lines from a "Scenario Outline:"?
|
58
|
-
# One way is when we encounter a "Scenario:"
|
59
|
-
close_scenario_outline
|
60
|
-
|
61
|
-
handle_scenario_line(clean_cuke_line($1))
|
62
|
-
@current_tags = []
|
63
|
-
when SCENARIO_OUTLINE_LINE
|
64
|
-
# ... another is when we hit a subsequent "Scenario Outline:"
|
65
|
-
close_scenario_outline
|
66
|
-
|
67
|
-
@scenario_outline = new_scenario_outline_node(clean_cuke_line($1))
|
68
|
-
@current_tags = []
|
69
|
-
when EXAMPLE_SET_LINE, SCENARIO_SET_LINE
|
70
|
-
handle_example_set_line if @example_set
|
71
|
-
|
72
|
-
@example_set = new_example_set_node(clean_cuke_line($1))
|
73
|
-
@current_tags = []
|
74
|
-
when @example_set && EXAMPLE_LINE
|
75
|
-
new_example_line(clean_cuke_line($1))
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# EOF is the final way that we know we are finished with a "Scenario Outline"
|
80
|
-
close_scenario_outline
|
81
|
-
return @feature
|
82
|
-
end
|
83
|
-
|
84
|
-
def clean_cuke_line(sub_expression)
|
85
|
-
sub_expression.strip.escape_apostrophe
|
86
|
-
end
|
87
23
|
end
|
88
24
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
begin
|
3
|
+
require 'gherkin/i18n'
|
4
|
+
rescue LoadError => ex
|
5
|
+
$gherkin_warning = %Q{Warning: Rubygems could not find Gherkin. Cuporter will use an old gherkin (2.3.3) language file.
|
6
|
+
Make sure the 'gherkin' gem is installed by checking your rvm config, your bundler config, or gem list.
|
7
|
+
|
8
|
+
"#{ex.class}: #{ex.message}"
|
9
|
+
"#{ex.backtrace.join("\n\t\t")}"
|
10
|
+
}
|
11
|
+
require 'cuporter/feature_parser/old_gherkin_yaml/i18n'
|
12
|
+
end
|
13
|
+
|
14
|
+
module Cuporter
|
15
|
+
module FeatureParser
|
16
|
+
class Language
|
17
|
+
|
18
|
+
attr_reader :feature_line, :scenario_line, :scenario_outline_line, :examples_line
|
19
|
+
|
20
|
+
def initialize(line_1)
|
21
|
+
@iso_code = 'en'
|
22
|
+
if (line_1.to_s =~ LANGUAGE_LINE)
|
23
|
+
@iso_code = $1
|
24
|
+
warn($gherkin_warning) if $gherkin_warning
|
25
|
+
end
|
26
|
+
@feature_line = pattern_for('feature')
|
27
|
+
@scenario_line = pattern_for('scenario')
|
28
|
+
@scenario_outline_line = pattern_for('scenario_outline')
|
29
|
+
@examples_line = pattern_for('examples')
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def pattern_for(keyword)
|
35
|
+
/^\s*((#{Gherkin::I18n::LANGUAGES[@iso_code][keyword]}):[^#]*)/u
|
36
|
+
end
|
37
|
+
|
38
|
+
LANGUAGE_LINE = /^\s*#\s*language:\s*([\w-]+)/u
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
|
3
|
+
module Cuporter
|
4
|
+
module FeatureParser
|
5
|
+
class NodeParser < ParserBase
|
6
|
+
|
7
|
+
# ++sub_expression++ is the paren group in the regex, dereferenced with $1 in the caller
|
8
|
+
def new_feature_node(sub_expression, file)
|
9
|
+
f = Node.new_node(:Feature, @doc, :cuke_name => sub_expression, :tags => @current_tags, :file_path => file)
|
10
|
+
f.filter = @filter
|
11
|
+
f
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_scenario_line(sub_expression)
|
15
|
+
if @filter.pass?(@current_tags | @feature.tags)
|
16
|
+
@feature.add_child(Node.new_node(:Scenario, @doc, :cuke_name => sub_expression, :tags => @current_tags))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_scenario_outline_node(sub_expression)
|
21
|
+
so = Node.new_node(:ScenarioOutline, @doc, :cuke_name => sub_expression, :tags => @current_tags)
|
22
|
+
so.filter = @filter
|
23
|
+
so
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_example_set_line
|
27
|
+
if @filter.pass?(@feature.tags | @scenario_outline.tags | @example_set.tags)
|
28
|
+
@scenario_outline.add_child @example_set
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_example_set_node(sub_expression)
|
33
|
+
es = Node.new_node(:Examples, @doc, :cuke_name => sub_expression, :tags => @current_tags)
|
34
|
+
es.filter = @filter
|
35
|
+
es
|
36
|
+
end
|
37
|
+
|
38
|
+
def new_example_line(sub_expression)
|
39
|
+
example_type = :ExampleHeader
|
40
|
+
# if the example set has a child already, then it must be the header
|
41
|
+
example_type = :Example if @example_set.has_children?
|
42
|
+
@example_set.add_child(Node.new_node(example_type, @doc, :cuke_name => sub_expression))
|
43
|
+
end
|
44
|
+
|
45
|
+
def close_scenario_outline
|
46
|
+
if @scenario_outline
|
47
|
+
if @example_set
|
48
|
+
handle_example_set_line
|
49
|
+
@example_set = nil
|
50
|
+
end
|
51
|
+
@feature.add_child(@scenario_outline) if @scenario_outline.has_children?
|
52
|
+
@scenario_outline = nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(file, doc, filter)
|
57
|
+
super(file)
|
58
|
+
@filter = filter
|
59
|
+
@doc = doc
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2010 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
|
3
|
+
module Cuporter
|
4
|
+
module FeatureParser
|
5
|
+
class ParserBase
|
6
|
+
TAG_LINE = /^\s*(@\w.+)/u
|
7
|
+
EXAMPLE_LINE = /^\s*(\|.*\|)\s*$/u
|
8
|
+
PY_STRING_LINE = /^\s*"""\s*$/u
|
9
|
+
|
10
|
+
def initialize(file)
|
11
|
+
@file = file
|
12
|
+
@current_tags = []
|
13
|
+
@lines = File.read(@file).split(/\n/)
|
14
|
+
@lang = Language.new(@lines.first)
|
15
|
+
end
|
16
|
+
attr_reader :lang
|
17
|
+
attr_writer :root
|
18
|
+
|
19
|
+
def file_relative_path
|
20
|
+
@file_relative_path ||= @file.sub(/^.*#{@root}\//,"#{@root}/")
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_feature
|
24
|
+
begin
|
25
|
+
handle_lines
|
26
|
+
rescue Exception => ex
|
27
|
+
Cuporter.log_error(ex, "Error parsing file", "at line #{@line_no}:", @file,
|
28
|
+
%Q{\n\tIf this file can be run by Cucumber with no Gherkin lexing or parsing errors,
|
29
|
+
please submit a bug ticket @ github including: 1) this feature file or its contents, and 2) this stack trace.
|
30
|
+
})
|
31
|
+
end
|
32
|
+
return @feature
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_lines
|
36
|
+
@open_comment_block = false
|
37
|
+
|
38
|
+
@lines.each_with_index do |line, i|
|
39
|
+
@line_no = i + 1
|
40
|
+
next if @open_comment_block && line !~ PY_STRING_LINE
|
41
|
+
|
42
|
+
case line
|
43
|
+
when PY_STRING_LINE
|
44
|
+
# toggle, to declare the multiline comment 'heredoc' open or closed
|
45
|
+
@open_comment_block = !@open_comment_block
|
46
|
+
when TAG_LINE
|
47
|
+
# may be more than one tag line
|
48
|
+
@current_tags |= clean_cuke_line($1).split(/\s+/)
|
49
|
+
when @lang.feature_line
|
50
|
+
@feature = new_feature_node(clean_cuke_line($1), file_relative_path)
|
51
|
+
@current_tags = []
|
52
|
+
when @lang.scenario_line
|
53
|
+
# How do we know when we have read all the lines from a "Scenario Outline:"?
|
54
|
+
# One way is when we encounter a "Scenario:"
|
55
|
+
close_scenario_outline
|
56
|
+
|
57
|
+
handle_scenario_line(clean_cuke_line($1))
|
58
|
+
@current_tags = []
|
59
|
+
when @lang.scenario_outline_line
|
60
|
+
# ... another is when we hit a subsequent "Scenario Outline:"
|
61
|
+
close_scenario_outline
|
62
|
+
|
63
|
+
@scenario_outline = new_scenario_outline_node(clean_cuke_line($1))
|
64
|
+
@current_tags = []
|
65
|
+
when @lang.examples_line
|
66
|
+
handle_example_set_line if @example_set
|
67
|
+
|
68
|
+
@example_set = new_example_set_node(clean_cuke_line($1))
|
69
|
+
@current_tags = []
|
70
|
+
when @example_set && EXAMPLE_LINE
|
71
|
+
new_example_line(clean_cuke_line($1))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# EOF is the final way that we know we are finished with a "Scenario Outline"
|
76
|
+
close_scenario_outline
|
77
|
+
end
|
78
|
+
|
79
|
+
def clean_cuke_line(sub_expression)
|
80
|
+
sub_expression.strip.escape_apostrophe
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright 2011 ThoughtWorks, Inc. Licensed under the MIT License
|
2
|
+
|
3
|
+
module Cuporter
|
4
|
+
module FeatureParser
|
5
|
+
class TagNodesParser < ParserBase
|
6
|
+
|
7
|
+
# ++sub_expression++ is the paren group in the regex, dereferenced with $1 in the caller
|
8
|
+
def new_feature_node(sub_expression, file)
|
9
|
+
{:cuke_name => sub_expression, :tags => @current_tags, :file_path => file}
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_scenario_line(sub_expression)
|
13
|
+
if @filter.pass?(@feature[:tags] | @current_tags)
|
14
|
+
s = {:cuke_name => sub_expression, :tags => @current_tags}
|
15
|
+
|
16
|
+
(@feature[:tags] | s[:tags]).each do |tag|
|
17
|
+
next unless @filter.pass?([tag])
|
18
|
+
add_scenario(tag, @feature, s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_scenario_outline_node(sub_expression)
|
24
|
+
{:cuke_name => sub_expression, :tags => @current_tags}
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_example_set_line
|
28
|
+
end
|
29
|
+
|
30
|
+
def new_example_set_node(sub_expression)
|
31
|
+
{:cuke_name => sub_expression.to_s.strip, :tags => @current_tags}
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_example_line(sub_expression)
|
35
|
+
context_tags = (@feature[:tags] | @scenario_outline[:tags] | @example_set[:tags])
|
36
|
+
if @filter.pass?(context_tags)
|
37
|
+
e = {:cuke_name => sub_expression}
|
38
|
+
|
39
|
+
context_tags.each do |tag|
|
40
|
+
next unless @filter.pass?([tag])
|
41
|
+
add_example(tag, @feature, @scenario_outline, @example_set, e)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def close_scenario_outline
|
47
|
+
if @scenario_outline
|
48
|
+
if @example_set
|
49
|
+
@example_set = nil
|
50
|
+
end
|
51
|
+
@scenario_outline = nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
def add_scenario(tag, feature, scenario)
|
55
|
+
unless ( t = @report.tag_node(tag))
|
56
|
+
t = @report.add_child(Node.new_node(:tag, @doc, 'cuke_name' => tag))
|
57
|
+
end
|
58
|
+
unless ( f = t.feature_node(feature) )
|
59
|
+
f = t.add_child(Node.new_node(:Feature, @doc, feature))
|
60
|
+
end
|
61
|
+
f.add_child(Node.new_node(:Scenario, @doc, scenario))
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_example(tag, feature, scenario_outline, example_set, example)
|
65
|
+
unless ( t = @report.tag_node(tag))
|
66
|
+
t = @report.add_child(Node.new_node(:Tag, @doc, 'cuke_name' => tag))
|
67
|
+
end
|
68
|
+
unless ( f = t.feature_node(feature) )
|
69
|
+
f = t.add_child(Node.new_node(:Feature, @doc, feature))
|
70
|
+
end
|
71
|
+
unless ( so = f.scenario_outline_node(scenario_outline) )
|
72
|
+
so = f.add_child(Node.new_node(:ScenarioOutline, @doc, scenario_outline))
|
73
|
+
end
|
74
|
+
|
75
|
+
# The first Example is an ExampleHeader, which does not get counted or
|
76
|
+
# numbered. If the ExampleSet is new, it has no children, and therefore
|
77
|
+
# this is the first and should be an ExampleHeader.
|
78
|
+
example_type = :Example
|
79
|
+
unless ( es = so.example_set_node(example_set) )
|
80
|
+
es = so.add_child(Node.new_node(:Examples, @doc, example_set))
|
81
|
+
example_type = :ExampleHeader
|
82
|
+
end
|
83
|
+
es.add_child(Node.new_node(example_type, @doc, example))
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def initialize(file, report, filter)
|
88
|
+
super(file)
|
89
|
+
@filter = filter
|
90
|
+
@report = report
|
91
|
+
@doc = report.document
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|