nukumber 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/nukumber ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
4
+ end
5
+
6
+ require 'nukumber'
7
+
8
+ begin
9
+ Nukumber::Cli.new(ARGV.dup)
10
+ rescue => e
11
+ STDERR.puts("#{e.message} (#{e.class})")
12
+ STDERR.puts(e.backtrace.join("\n"))
13
+ Kernel.exit(1)
14
+ end
data/lib/nukumber.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'gherkin/rubify'
2
+ require 'gherkin/parser/parser'
3
+ require 'gherkin/formatter/filter_formatter'
4
+ require 'gherkin/formatter/tag_count_formatter'
5
+ require 'nokogiri'
6
+
7
+ require 'nukumber/errors'
8
+
9
+ require 'nukumber/model'
10
+ require 'nukumber/model/feature'
11
+ require 'nukumber/model/feature_element'
12
+ require 'nukumber/model/background'
13
+ require 'nukumber/model/scenario'
14
+ require 'nukumber/model/scenario_outline'
15
+ require 'nukumber/model/table'
16
+ require 'nukumber/model/examples'
17
+ require 'nukumber/model/step'
18
+
19
+ require 'nukumber/reporter'
20
+ require 'nukumber/reporter/colour'
21
+ require 'nukumber/reporter/mono'
22
+ require 'nukumber/reporter/html'
23
+ require 'nukumber/reporter/html_css'
24
+
25
+ require 'nukumber/builder'
26
+ require 'nukumber/cli'
27
+ require 'nukumber/helpers'
28
+ require 'nukumber/hooks'
29
+ require 'nukumber/runtime'
@@ -0,0 +1,104 @@
1
+ require "gherkin"
2
+
3
+ module Nukumber
4
+
5
+ class GherkinBuilder
6
+ # Builds the Nukumber Model from the output of the gherkin parser.
7
+
8
+ include Gherkin::Rubify
9
+
10
+ attr_reader :features
11
+
12
+ def initialize
13
+ @features = []
14
+ @all_longsyms = []
15
+ @all_shortsyms = []
16
+ end
17
+
18
+ def check_names
19
+ if @all_longsyms.include? @current_feature_element.longsym
20
+ raise Nukumber::NameCollisionError, @current_feature_element.longsym
21
+ elsif @all_longsyms.include? @current_feature_element.shortsym or @all_shortsyms.include? @current_feature_element.shortsym
22
+ raise Nukumber::NameCollisionWarning, @current_feature_element.shortsym
23
+ end
24
+ @all_longsyms << @current_feature_element.longsym
25
+ @all_shortsyms << @current_feature_element.shortsym
26
+ end
27
+
28
+ def feature(gherkin_feature)
29
+ @current_feature = Nukumber::Model::Feature.new(
30
+ gherkin_feature.name.strip,
31
+ gherkin_feature.line,
32
+ gherkin_feature.description.strip,
33
+ $feature_file_path
34
+ )
35
+ @current_feature.tags = gherkin_feature.tags.map(&:name)
36
+ @features << @current_feature
37
+ end
38
+
39
+ def background(gherkin_background)
40
+ @current_feature_element = Nukumber::Model::Background.new(
41
+ gherkin_background.name.strip,
42
+ gherkin_background.line,
43
+ gherkin_background.description.strip,
44
+ @current_feature
45
+ )
46
+ @current_feature.background = @current_feature_element
47
+ check_names
48
+ end
49
+
50
+ def scenario(gherkin_scenario)
51
+ @current_feature_element = Nukumber::Model::Scenario.new(
52
+ gherkin_scenario.name.strip,
53
+ gherkin_scenario.line,
54
+ gherkin_scenario.description.strip,
55
+ @current_feature
56
+ )
57
+ @current_feature_element.tags = gherkin_scenario.tags.map(&:name)
58
+ @current_feature.feature_elements << @current_feature_element
59
+ check_names
60
+ end
61
+
62
+ def scenario_outline(gherkin_outline)
63
+ @current_feature_element = Nukumber::Model::ScenarioOutline.new(
64
+ gherkin_outline.name.strip,
65
+ gherkin_outline.line,
66
+ gherkin_outline.description.strip,
67
+ @current_feature
68
+ )
69
+ @current_feature_element.tags = gherkin_outline.tags.map(&:name)
70
+ @current_feature.feature_elements << @current_feature_element
71
+ check_names
72
+ end
73
+
74
+ def examples(gherkin_examples)
75
+ @current_feature_element.examples = Nukumber::Model::Examples.new(
76
+ gherkin_examples.name.strip,
77
+ gherkin_examples.line,
78
+ gherkin_examples.keyword,
79
+ Nukumber::Model::Table.new(gherkin_examples.rows)
80
+ )
81
+ end
82
+
83
+ def step(gherkin_step)
84
+ @current_feature_element.steps << Nukumber::Model::Step.new(
85
+ gherkin_step.name.strip,
86
+ gherkin_step.line,
87
+ gherkin_step.keyword,
88
+ @current_feature_element,
89
+ Nukumber::Model::Table.new(gherkin_step.rows)
90
+ )
91
+ end
92
+
93
+ def eof
94
+ @current_feature = nil
95
+ @current_feature_element = nil
96
+ end
97
+
98
+ def syntax_error(*)
99
+ raise Nukumber::SyntaxError
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,101 @@
1
+ module Nukumber
2
+
3
+ class Cli
4
+
5
+ def initialize(args, out_stream = STDOUT)
6
+
7
+ directories = {
8
+ :features => 'features',
9
+ :definitions => 'test_definitions',
10
+ :support => 'support'
11
+ }
12
+
13
+ reporter_type = 'Mono'
14
+ reporter_type = 'Colour' if out_stream.tty?
15
+
16
+ filters = []
17
+ filenames = []
18
+
19
+ i = 0
20
+ while i < args.size do
21
+
22
+ # named arguments
23
+ if %w( -h --help ).include? args[i]
24
+ print_help(out_stream)
25
+ return
26
+
27
+ elsif %w( -fd --features ).include? args[i] and (i+1) < args.size
28
+ directories[:features] = args[i+1]
29
+ i += 2
30
+ next
31
+
32
+ elsif %w( -sd --support ).include? args[i] and (i+1) < args.size
33
+ directories[:support] = args[i+1]
34
+ i += 2
35
+ next
36
+
37
+ elsif %w( -dd --definitions ).include? args[i] and (i+1) < args.size
38
+ directories[:definitions] = args[i+1]
39
+ i += 2
40
+ next
41
+
42
+ elsif %w( -t --tag ).include? args[i] and (i+1) < args.size
43
+ filters << args[i+1] if args[i+1][0, 1] == '@'
44
+ i += 2
45
+ next
46
+
47
+ elsif %w( -f --format ).include? args[i] and i + 1 < args.size
48
+ if %w( c colour ).include? args[i+1]
49
+ reporter_type = 'Colour'
50
+ elsif %w( m mono ).include? args[i+1]
51
+ reporter_type = 'Mono'
52
+ elsif %w( h html ).include? args[i+1]
53
+ reporter_type = 'Html'
54
+ elsif %w( s silent ).include? args[i+1]
55
+ reporter_type = 'Abstract'
56
+ else
57
+ raise "\"#{args[i+1]}\" is an invalid argument to \"#{args[i]}\""
58
+ end
59
+ i += 2
60
+ next
61
+ end
62
+
63
+ # filename patterns
64
+ arr = args[i].split(/:/)
65
+ filenames << arr.shift
66
+ arr.each { |n| filters << n.to_i unless n.to_i == 0 }
67
+
68
+ # increment and loop
69
+ i += 1
70
+ end
71
+
72
+ reporter = eval("Nukumber::Reporter::#{reporter_type}").new(out_stream)
73
+
74
+ @runtime = Nukumber::Runtime.new(
75
+ directories,
76
+ filenames,
77
+ filters,
78
+ reporter
79
+ )
80
+
81
+ end
82
+
83
+ def print_help(out_stream)
84
+ out_stream.puts <<HERE
85
+ Usage: nukumber [options] [FILENAMEPATTERN[:LINE]]
86
+ -fd, --features X : Specify feature directory name
87
+ -dd, --definitions X : Specify test definitions subdirectory name
88
+ -sd, --support X : Specify support code subdirectory name
89
+ -t, --tag X : Only run tests with this tag
90
+ -f, --format X : Specify output format; options are c/colour,
91
+ m/mono, h/html, s/silent
92
+ FILENAMEPATTERN : Only run feature files whose names include
93
+ this string
94
+ LINE : Optional addition to FILENAMEPATTERN; filters
95
+ to a specific line number
96
+ HERE
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,18 @@
1
+ module Nukumber
2
+
3
+ class UndefinedTestError < RuntimeError
4
+ end
5
+
6
+ class PendingTestError < RuntimeError
7
+ end
8
+
9
+ class NameCollisionError < RuntimeError
10
+ end
11
+
12
+ class NameCollisionWarning < RuntimeError
13
+ end
14
+
15
+ class SyntaxError < RuntimeError
16
+ end
17
+
18
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def nukesym
3
+ self.downcase.gsub(/\W/, '_').to_sym
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ def Before(*tags, &proc)
2
+ unless tags.empty?
3
+ tags.first.split(',').each do |t|
4
+ $before_hooks[t.strip.to_sym] ||= []
5
+ $before_hooks[t.strip.to_sym] << proc
6
+ end
7
+ return
8
+ end
9
+ $before_hooks[:each] ||= []
10
+ $before_hooks[:each] << proc
11
+ end
12
+
13
+ def After(*tags, &proc)
14
+ unless tags.empty?
15
+ tags.first.split(',').each do |t|
16
+ $after_hooks[t.strip.to_sym] ||= []
17
+ $after_hooks[t.strip.to_sym] << proc
18
+ end
19
+ return
20
+ end
21
+ $after_hooks[:each] ||= []
22
+ $after_hooks[:each] << proc
23
+ end
@@ -0,0 +1,9 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ LONGSYM_DELIM = '___'
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,17 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ class Background < FeatureElement
6
+ def keyword
7
+ 'Background'
8
+ end
9
+
10
+ def tags
11
+ []
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ class Examples
6
+ attr_reader :name, :line, :keyword
7
+ attr_accessor :table
8
+
9
+ def initialize(name, line, keyword, table)
10
+ @name, @line, @keyword, @table = name, line, keyword, table
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,33 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ class Feature
6
+ attr_reader :name, :line, :description, :file_path
7
+ attr_accessor :feature_elements, :tags, :background
8
+
9
+ def initialize(name, line, description, file_path)
10
+ if name.length == 0
11
+ raise "Features must be named (#{file_path}:#{line})"
12
+ elsif name[0,1] !~ /[a-zA-Z]/
13
+ raise "Feature names must begin with a letter (#{file_path}:#{line})"
14
+ end
15
+ @name, @line, @description = name, line, description
16
+ @feature_elements, @tags = [], []
17
+ @background = nil
18
+ @file_path = file_path
19
+ end
20
+
21
+ def keyword
22
+ 'Feature'
23
+ end
24
+
25
+ def to_s
26
+ "#{keyword} \"#{name}\""
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,47 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ class FeatureElement
6
+ attr_reader :name, :line, :description
7
+ attr_reader :feature
8
+ attr_accessor :steps
9
+
10
+ def initialize(name, line, description, feature)
11
+ if name.length == 0
12
+ raise "Feature elements must be named (#{feature.file_path}:#{line})"
13
+ elsif name[0, 1] !~ /[a-zA-Z]/
14
+ raise "Feature element names must begin with a letter (#{feature.file_path}:#{line})"
15
+ end
16
+ @name, @line, @description, @feature = name, line, description, feature
17
+ @steps = []
18
+ end
19
+
20
+ def shortsym
21
+ name.nukesym
22
+ end
23
+
24
+ def longsym
25
+ (@feature.name.nukesym.to_s + LONGSYM_DELIM + name.nukesym.to_s).to_sym
26
+ end
27
+
28
+ def to_s
29
+ "#{keyword} \"#{name}\""
30
+ end
31
+
32
+ def step_args
33
+ step_args = []
34
+ @steps.each do |s|
35
+ step_args << s.args.all_row_hashes unless s.args.empty?
36
+ end
37
+ while step_args.is_a? Array and step_args.size == 1
38
+ step_args = step_args.first
39
+ end
40
+ step_args
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,20 @@
1
+ module Nukumber
2
+
3
+ module Model
4
+
5
+ class Scenario < FeatureElement
6
+ attr_accessor :tags
7
+
8
+ def initialize(name, line, description, feature)
9
+ super(name, line, description, feature)
10
+ @tags = []
11
+ end
12
+
13
+ def keyword
14
+ 'Scenario'
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end