cellophane 0.1.0
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.
- data/.cellophane.yaml +1 -0
- data/.cellophane.yaml.sample +7 -0
- data/.document +4 -0
- data/LICENSE.txt +20 -0
- data/README.textile +269 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/bin/cellophane +14 -0
- data/features/feature_paths.feature +29 -0
- data/features/glob_pattern.feature +173 -0
- data/features/project_options.feature +29 -0
- data/features/regular_expression_pattern.feature +94 -0
- data/features/step_definitions.feature +47 -0
- data/features/support/cellophane_methods.rb +29 -0
- data/features/support/cellophane_steps.rb +43 -0
- data/features/support/env.rb +17 -0
- data/features/tags.feature +55 -0
- data/lib/cellophane/main.rb +82 -0
- data/lib/cellophane/options.rb +145 -0
- data/lib/cellophane/parser.rb +137 -0
- data/spec/cellophane_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +87 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
Feature: Step Definitions
|
2
|
+
|
3
|
+
@1
|
4
|
+
Scenario: Requiring existing step definition
|
5
|
+
|
6
|
+
Given a project directory with the following structure
|
7
|
+
| type | path |
|
8
|
+
| directory | features |
|
9
|
+
| directory | features/step_definitions |
|
10
|
+
| file | features/one.feature |
|
11
|
+
| file | features/two.feature |
|
12
|
+
| file | features/step_definitions/one_steps.rb |
|
13
|
+
When Cellophane is called with "one"
|
14
|
+
Then the command should include "features/one.feature"
|
15
|
+
And the command should include "-r features/step_definitions/one_steps.rb"
|
16
|
+
|
17
|
+
@2
|
18
|
+
Scenario: Not requiring step definition that doesn't exist
|
19
|
+
|
20
|
+
Given a project directory with the following structure
|
21
|
+
| type | path |
|
22
|
+
| directory | features |
|
23
|
+
| directory | features/step_definitions |
|
24
|
+
| file | features/one.feature |
|
25
|
+
| file | features/two.feature |
|
26
|
+
| file | features/step_definitions/one_steps.rb |
|
27
|
+
When Cellophane is called with "two"
|
28
|
+
Then the command should include "features/two.feature"
|
29
|
+
And the command should not include "-r features/step_definitions/two_steps.rb"
|
30
|
+
|
31
|
+
@3
|
32
|
+
Scenario: Step definitions nested in feature subdirectories
|
33
|
+
|
34
|
+
Given a project directory with the following structure
|
35
|
+
| type | path |
|
36
|
+
| directory | features |
|
37
|
+
| directory | features/admin |
|
38
|
+
| directory | features/admin/step_definitions |
|
39
|
+
| file | features/admin/one.feature |
|
40
|
+
| file | features/admin/two.feature |
|
41
|
+
| file | features/admin/step_definitions/one_steps.rb |
|
42
|
+
And a project options file with the following options
|
43
|
+
| option |
|
44
|
+
| step_path: {nested_in: step_definitions} |
|
45
|
+
When Cellophane is called with "admin/one"
|
46
|
+
Then the command should include "features/admin/one.feature"
|
47
|
+
And the command should include "-r features/admin/step_definitions/one_steps.rb"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CellophaneMethods
|
2
|
+
def call_cellophane(args = [])
|
3
|
+
cellophane = Cellophane::Main.new(args)
|
4
|
+
@command = cellophane.command
|
5
|
+
@message = cellophane.message
|
6
|
+
end
|
7
|
+
|
8
|
+
def output_command
|
9
|
+
puts "\n\n#{@command}\n\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def output_message
|
13
|
+
puts "\n\n#{@message}\n\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
def save_initial_dir
|
17
|
+
@initial_dir = Dir.pwd
|
18
|
+
end
|
19
|
+
|
20
|
+
def restore_initial_dir
|
21
|
+
Dir.chdir(@initial_dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ensure_project_dir_removed
|
25
|
+
FileUtils.remove_dir(@project_dir, true) if @project_dir && File.exist?(@project_dir)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
World(CellophaneMethods)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
Given /^I debug$/ do
|
2
|
+
require 'ruby-debug'
|
3
|
+
debugger
|
4
|
+
puts "\n\ndebugging\n\n"
|
5
|
+
end
|
6
|
+
|
7
|
+
Given /^Cellophane is called with "([^"]*)"$/ do |args|
|
8
|
+
call_cellophane(args.split(' '))
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /^the (command|message) should include "([^"]+)"$/ do |what, expected|
|
12
|
+
(what == 'command' ? @command : @message).should =~ /#{expected}/
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /^the (command|message) should not include "([^"]+)"$/ do |what, expected|
|
16
|
+
(what == 'command' ? @command : @message).should_not =~ /#{expected}/
|
17
|
+
end
|
18
|
+
|
19
|
+
Given /^a project directory with the following structure$/ do |structure|
|
20
|
+
@project_dir = './test_project'
|
21
|
+
# just in case the last run failed to exit cleanly, delete the test_project directory
|
22
|
+
# if it exists
|
23
|
+
FileUtils.remove_dir(@project_dir, true) if File.exist?(@project_dir)
|
24
|
+
FileUtils.mkdir(@project_dir)
|
25
|
+
Dir.chdir(@project_dir)
|
26
|
+
|
27
|
+
structure.hashes.each do |entry|
|
28
|
+
if entry[:type] == 'directory'
|
29
|
+
FileUtils.mkdir_p(entry[:path])
|
30
|
+
else
|
31
|
+
File.open(entry[:path], 'w') {|f| f.write("# #{entry[:path]}") }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Given /^a project options file with the following options$/ do |lines|
|
37
|
+
options = lines.hashes.collect { |line| line[:option] }
|
38
|
+
|
39
|
+
contents = options.join("\n")
|
40
|
+
|
41
|
+
# already in the project dir
|
42
|
+
File.open(Cellophane::PROJECT_OPTIONS_FILE, 'w') { |f| f.write(contents) }
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
require 'cellophane/main'
|
3
|
+
require 'rspec/expectations'
|
4
|
+
|
5
|
+
Before do
|
6
|
+
save_initial_dir
|
7
|
+
ensure_project_dir_removed
|
8
|
+
end
|
9
|
+
|
10
|
+
Before('@debug') do
|
11
|
+
require 'ruby-debug'
|
12
|
+
end
|
13
|
+
|
14
|
+
After do
|
15
|
+
restore_initial_dir
|
16
|
+
ensure_project_dir_removed
|
17
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
Feature: Tags
|
2
|
+
|
3
|
+
@1
|
4
|
+
Scenario: OR tag
|
5
|
+
|
6
|
+
When Cellophane is called with "-t one,two"
|
7
|
+
Then the command should include "-t @one,@two"
|
8
|
+
|
9
|
+
@2
|
10
|
+
Scenario: NOT tag
|
11
|
+
|
12
|
+
When Cellophane is called with "-t one,~two"
|
13
|
+
Then the command should include "-t @one -t ~@two"
|
14
|
+
|
15
|
+
@3
|
16
|
+
Scenario: AND tag
|
17
|
+
|
18
|
+
When Cellophane is called with "-t one,+two"
|
19
|
+
Then the command should include "-t @one -t @two"
|
20
|
+
|
21
|
+
@4
|
22
|
+
Scenario: Mixed tags in a logical order
|
23
|
+
|
24
|
+
When Cellophane is called with "-t one,two,~three,+four"
|
25
|
+
Then the command should include "-t @one,@two -t @four -t ~@three"
|
26
|
+
|
27
|
+
@5
|
28
|
+
Scenario: Mixed tags not in a logical order
|
29
|
+
|
30
|
+
When Cellophane is called with "-t +four,one,~three,two"
|
31
|
+
Then the command should include "-t @one,@two -t @four -t ~@three"
|
32
|
+
|
33
|
+
@6
|
34
|
+
Scenario: Numeric OR tag ranges
|
35
|
+
|
36
|
+
When Cellophane is called with "-t 1-3"
|
37
|
+
Then the command should include "-t @1,@2,@3"
|
38
|
+
|
39
|
+
@7
|
40
|
+
Scenario: Numeric NOT tag ranges
|
41
|
+
|
42
|
+
When Cellophane is called with "-t ~1-3"
|
43
|
+
Then the command should include "-t ~@1 -t ~@2 -t ~@3"
|
44
|
+
|
45
|
+
@8
|
46
|
+
Scenario: Numeric OR tag range with a NOT in the OR range
|
47
|
+
|
48
|
+
When Cellophane is called with "-t 1-3,~2"
|
49
|
+
Then the command should include "-t @1,@3"
|
50
|
+
|
51
|
+
@9
|
52
|
+
Scenario: Numeric OR tag range with a NOT out of the OR range
|
53
|
+
|
54
|
+
When Cellophane is called with "-t 1-3,~slow"
|
55
|
+
Then the command should include "-t @1,@2,@3 -t ~@slow"
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'cellophane/parser'
|
3
|
+
require 'cellophane/options'
|
4
|
+
|
5
|
+
module Cellophane
|
6
|
+
|
7
|
+
PROJECT_OPTIONS_FILE = '.cellophane.yaml'
|
8
|
+
|
9
|
+
class Main
|
10
|
+
attr_reader :command, :message, :project_options_file
|
11
|
+
|
12
|
+
def initialize(args = nil)
|
13
|
+
args ||= ARGV
|
14
|
+
@project_options_file = Cellophane::PROJECT_OPTIONS_FILE
|
15
|
+
@options = Cellophane::Options.parse(args)
|
16
|
+
|
17
|
+
@message = 'Invalid regular expression provided.' and return if @options[:regexp] && @options[:pattern].nil?
|
18
|
+
|
19
|
+
parser = Cellophane::Parser.new(@options)
|
20
|
+
@features = parser.features
|
21
|
+
|
22
|
+
@message = 'No features matching PATTERN were found.' and return unless @features
|
23
|
+
|
24
|
+
@tags = parser.tags
|
25
|
+
|
26
|
+
@command = generate_command
|
27
|
+
end
|
28
|
+
|
29
|
+
def run
|
30
|
+
puts @message and return if @message
|
31
|
+
@options[:print] ? puts(@command) : system("#{@command}\n\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def generate_command
|
37
|
+
cuke_cmd = "cucumber #{@options[:cucumber]}"
|
38
|
+
|
39
|
+
features = []
|
40
|
+
steps = []
|
41
|
+
|
42
|
+
if @features.any?
|
43
|
+
@features.each do |file|
|
44
|
+
file_parts = split_feature(file)
|
45
|
+
features << construct_feature_file(file_parts[:path], file_parts[:name])
|
46
|
+
steps << construct_step_file(file_parts[:path], file_parts[:name])
|
47
|
+
end
|
48
|
+
|
49
|
+
else
|
50
|
+
# if there are no features explicitly identified, then cucumber will run all. However,
|
51
|
+
# if we are using non-standard locations for features or step definitions, we must tell
|
52
|
+
# cucumber accordingly
|
53
|
+
features << @options[:feature_path] if @options[:non_standard_feature_path]
|
54
|
+
steps << @options[:step_path] if @options[:non_standard_step_path]
|
55
|
+
end
|
56
|
+
|
57
|
+
requires = (@options[:requires] + steps).compact.uniq
|
58
|
+
cuke_cmd += " -r #{requires.join(' -r ')}" if requires.any?
|
59
|
+
cuke_cmd += " #{features.join(' ')}" if features.any?
|
60
|
+
return "#{cuke_cmd} #{@tags}".gsub(' ', ' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def construct_feature_file(path, file)
|
64
|
+
"#{@options[:feature_path]}/#{path}/#{file}.feature".gsub('//', '/')
|
65
|
+
end
|
66
|
+
|
67
|
+
def construct_step_file(path, file)
|
68
|
+
step_path = @options[:step_path].is_a?(Hash) ? "#{@options[:feature_path]}/#{path}/#{@options[:step_path][:nested_in]}" : "#{@options[:step_path]}/#{path}"
|
69
|
+
step_file = "#{step_path}/#{file}_steps.rb".gsub('//', '/')
|
70
|
+
return File.exist?(step_file) ? step_file : nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def split_feature(file)
|
74
|
+
name = File.basename(file, '.feature')
|
75
|
+
# now get rid of the file_name and the feature_path
|
76
|
+
path = File.dirname(file).gsub(@options[:feature_path_regexp], '')
|
77
|
+
return {:path => path, :name => name}
|
78
|
+
end
|
79
|
+
|
80
|
+
end # class Main
|
81
|
+
end # module Cellophane
|
82
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Cellophane
|
5
|
+
class Options
|
6
|
+
def self.parse(args)
|
7
|
+
default_options = self.get_options(:default)
|
8
|
+
project_options = self.get_options(:project)
|
9
|
+
merged_options = default_options.merge(project_options)
|
10
|
+
|
11
|
+
option_parser = OptionParser.new do |opts|
|
12
|
+
# Set a banner, displayed at the top of the help screen.
|
13
|
+
# TODO add example usage including ~feature, patterns, and ~tag
|
14
|
+
opts.banner = "Usage: cellophane [options] PATTERN"
|
15
|
+
|
16
|
+
opts.on('-r', '--regexp', 'PATTERN is a regular expression. Default is false.') do
|
17
|
+
merged_options[:regexp] = true
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('-t', '--tags TAGS', 'Tags to include/exclude.') do |tags|
|
21
|
+
merged_options[:tags] = tags
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on('-c', '--cucumber OPTIONS', 'Options to pass to cucumber.') do |cucumber|
|
25
|
+
merged_options[:cucumber] = cucumber
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-p', '--print', 'Echo the command instead of calling cucumber.') do
|
29
|
+
merged_options[:print] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-d', '--debug', 'Require ruby-debug.') do
|
33
|
+
require 'rubygems'
|
34
|
+
require 'ruby-debug'
|
35
|
+
end
|
36
|
+
|
37
|
+
# This displays the help screen, all programs are assumed to have this option.
|
38
|
+
opts.on( '-h', '--help', 'Display this screen.' ) do
|
39
|
+
puts opts
|
40
|
+
exit(0)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
option_parser.parse!(args)
|
45
|
+
|
46
|
+
# get the pattern from the command line (no switch)
|
47
|
+
merged_options[:pattern] = args.first if args.any?
|
48
|
+
|
49
|
+
return self.normalize_options(merged_options)
|
50
|
+
end # self.parse
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def self.get_options(type = :default)
|
55
|
+
if type == :project
|
56
|
+
project_options = {}
|
57
|
+
|
58
|
+
# load is used here due to require not requiring a file if
|
59
|
+
# it has already been required. This is mainly for testing
|
60
|
+
# purposes (multiple features needing to have different
|
61
|
+
# options for validation), but it shouldn't make a difference
|
62
|
+
# for run time.
|
63
|
+
#load project_options_file if File.exist?(project_options_file)
|
64
|
+
|
65
|
+
if File.exist?(Cellophane::PROJECT_OPTIONS_FILE)
|
66
|
+
yaml_options = YAML.load_file(Cellophane::PROJECT_OPTIONS_FILE)
|
67
|
+
|
68
|
+
['cucumber', 'feature_path', 'feature_path_regexp', 'step_path', 'requires'].each do |key|
|
69
|
+
project_options[key.to_sym] = yaml_options[key] if yaml_options.has_key?(key)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
project_options
|
74
|
+
else
|
75
|
+
{
|
76
|
+
:pattern => nil,
|
77
|
+
:regexp => false,
|
78
|
+
:print => false,
|
79
|
+
:cucumber => nil,
|
80
|
+
:tags => nil,
|
81
|
+
:feature_path => 'features',
|
82
|
+
:feature_path_regexp => nil,
|
83
|
+
:step_path => 'features/step_definitions',
|
84
|
+
:requires => []
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end # get_options
|
88
|
+
|
89
|
+
def self.normalize_options(options)
|
90
|
+
defaults = self.get_options(:default)
|
91
|
+
|
92
|
+
# ran into freezing problems in Ruby 1.9.2 otherwise
|
93
|
+
tmp_options = options.dup
|
94
|
+
|
95
|
+
# normalize the paths for features and steps
|
96
|
+
# had originally used the gsub! and sub!, but hit freezing problems with Ruby 1.9.2
|
97
|
+
|
98
|
+
# globs don't work with backslashes (if on windows)
|
99
|
+
tmp_options[:feature_path] = tmp_options[:feature_path].gsub(/\\/, '/')
|
100
|
+
# strip trailing slash
|
101
|
+
tmp_options[:feature_path] = tmp_options[:feature_path].sub(/\/$/, '')
|
102
|
+
|
103
|
+
if tmp_options[:step_path].is_a?(Hash)
|
104
|
+
# if the step path is configured in YAML, it will be a string key, not a symbol
|
105
|
+
key = tmp_options[:step_path].has_key?('nested_in') ? 'nested_in' : :nested_in
|
106
|
+
if tmp_options[:step_path].has_key?(key)
|
107
|
+
tmp_options[:step_path][:nested_in] = tmp_options[:step_path][key].gsub(/\\/, '/')
|
108
|
+
tmp_options[:step_path][:nested_in] = tmp_options[:step_path][key].sub(/\/$/, '')
|
109
|
+
end
|
110
|
+
else
|
111
|
+
tmp_options[:step_path] = tmp_options[:step_path].gsub(/\\/, '/')
|
112
|
+
tmp_options[:step_path] = tmp_options[:step_path].sub(/\/$/, '')
|
113
|
+
end
|
114
|
+
|
115
|
+
# need to know this later
|
116
|
+
tmp_options[:non_standard_feature_path] = tmp_options[:feature_path] != defaults[:feature_path]
|
117
|
+
tmp_options[:non_standard_step_path] = tmp_options[:step_path] != defaults[:step_path]
|
118
|
+
|
119
|
+
# make a regexp out of the features path if there isn't one already. we need to escape slashes so the
|
120
|
+
# regexp can be made
|
121
|
+
tmp_options[:feature_path_regexp] = Regexp.new(tmp_options[:feature_path].gsub('/', '\/')) unless tmp_options[:feature_path_regexp]
|
122
|
+
|
123
|
+
# just in case someone sets necessary values to nil, let's go back to defaults
|
124
|
+
tmp_options[:regexp] ||= defaults[:regexp]
|
125
|
+
tmp_options[:feature_path] ||= defaults[:feature_path]
|
126
|
+
tmp_options[:step_path] ||= defaults[:step_path]
|
127
|
+
tmp_options[:requires] ||= defaults[:requires]
|
128
|
+
|
129
|
+
# do what needs to be done on the pattern
|
130
|
+
unless tmp_options[:pattern].nil?
|
131
|
+
tmp_options[:pattern] = tmp_options[:pattern].strip
|
132
|
+
tmp_options[:pattern] = nil unless tmp_options[:pattern] && !tmp_options[:pattern].empty?
|
133
|
+
|
134
|
+
begin
|
135
|
+
tmp_options[:pattern] = Regexp.new(tmp_options[:pattern]) if tmp_options[:regexp]
|
136
|
+
rescue
|
137
|
+
# if the regexp fails for some reason
|
138
|
+
tmp_options[:pattern] = nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
return tmp_options
|
143
|
+
end # normalize_options
|
144
|
+
end # class Options
|
145
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Cellophane
|
2
|
+
class Parser
|
3
|
+
def initialize(options)
|
4
|
+
@options = options
|
5
|
+
end
|
6
|
+
|
7
|
+
def features
|
8
|
+
# if no pattern is specified, let cucumber run 'em all
|
9
|
+
return [] if @options[:pattern].nil?
|
10
|
+
collected_features = @options[:regexp] ? collect_features_by_regexp : collect_features_by_glob
|
11
|
+
return collected_features.any? ? collected_features : nil
|
12
|
+
end # features
|
13
|
+
|
14
|
+
def tags
|
15
|
+
tags = {
|
16
|
+
:or => [],
|
17
|
+
:and => [],
|
18
|
+
:not => []
|
19
|
+
}
|
20
|
+
|
21
|
+
return '' if @options[:tags].nil?
|
22
|
+
|
23
|
+
@options[:tags].split(',').each do |t|
|
24
|
+
# if tags are numeric, let's support ranges !!!
|
25
|
+
if t =~ /^(~)?([0-9]+)-([0-9]+)$/
|
26
|
+
x = $2.to_i
|
27
|
+
y = $3.to_i
|
28
|
+
exclude = $1
|
29
|
+
|
30
|
+
# in case the user put them in the wrong order ... doh!
|
31
|
+
if x > y
|
32
|
+
z = x.dup
|
33
|
+
x = y.dup
|
34
|
+
y = z.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
(x..y).each do |i|
|
38
|
+
if exclude
|
39
|
+
tags[:not] << "#{i}"
|
40
|
+
else
|
41
|
+
tags[:or] << "#{i}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
if t =~ /^~(.+)/
|
46
|
+
tags[:not] << $1
|
47
|
+
elsif t =~ /^\+(.+)/
|
48
|
+
tags[:and] << $1
|
49
|
+
else
|
50
|
+
tags[:or] << t
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end # each
|
54
|
+
|
55
|
+
[:and, :or, :not].each { |type| tags[type].uniq! }
|
56
|
+
|
57
|
+
# if there are AND/OR tags, remove any NOT tags so we avoid
|
58
|
+
# duplicating the tag when passing to cucumber...so instead of
|
59
|
+
# cucumber -t @1,@2,@3 -t ~@2
|
60
|
+
# we'd like to see
|
61
|
+
# cucumber -t @1,@3
|
62
|
+
|
63
|
+
intersection = tags[:or] & tags[:not]
|
64
|
+
tags[:or] -= intersection
|
65
|
+
tags[:not] -= intersection
|
66
|
+
|
67
|
+
intersection = tags[:and] & tags[:not]
|
68
|
+
tags[:and] -= intersection
|
69
|
+
tags[:not] -= intersection
|
70
|
+
|
71
|
+
# now add @ and ~ as appropriate
|
72
|
+
tags[:or].each_with_index { |tag, i| tags[:or][i] = "@#{tag}" }
|
73
|
+
tags[:and].each_with_index { |tag, i| tags[:and][i] = "@#{tag}" }
|
74
|
+
tags[:not].each_with_index { |tag, i| tags[:not][i] = "~@#{tag}" }
|
75
|
+
|
76
|
+
tags_fragment = ''
|
77
|
+
tags_fragment += "-t #{tags[:or].join(',')} " if tags[:or].any?
|
78
|
+
tags_fragment += "-t #{tags[:and].join(' -t ')} " if tags[:and].any?
|
79
|
+
tags_fragment += "-t #{tags[:not].join(' -t ')}" if tags[:not].any?
|
80
|
+
|
81
|
+
# if the user passes in tags with @ already in it
|
82
|
+
tags_fragment.gsub('@@', '@')
|
83
|
+
end # def self.parse_tags
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def collect_features_by_regexp
|
88
|
+
features = []
|
89
|
+
|
90
|
+
# start by globbing all feature files
|
91
|
+
Dir.glob("#{@options[:feature_path]}/**/*.feature").each do |feature_file|
|
92
|
+
# keep the ones that match the regexp
|
93
|
+
features << feature_file if @options[:pattern].match(feature_file)
|
94
|
+
end
|
95
|
+
|
96
|
+
features.uniq
|
97
|
+
end # collect_features_by_regexp
|
98
|
+
|
99
|
+
def collect_features_by_glob
|
100
|
+
only = []
|
101
|
+
except = []
|
102
|
+
features_to_include = []
|
103
|
+
features_to_exclude = []
|
104
|
+
pattern = @options[:pattern].dup
|
105
|
+
|
106
|
+
# want to run certain ones and/or exclude certain ones
|
107
|
+
pattern.split(',').each do |f|
|
108
|
+
if f[0].chr == '~'
|
109
|
+
except << f[1..f.length]
|
110
|
+
else
|
111
|
+
only << f
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# if we have an exception, we want to get all features by default
|
116
|
+
pattern = '**/*' if except.any?
|
117
|
+
# unless we specifically say we want only certain ones
|
118
|
+
pattern = nil if only.any?
|
119
|
+
|
120
|
+
if only.any?
|
121
|
+
only.each do |f|
|
122
|
+
features_to_include += Dir.glob("#{@options[:feature_path]}/#{f}.feature")
|
123
|
+
end
|
124
|
+
else
|
125
|
+
features_to_include += Dir.glob("#{@options[:feature_path]}/#{pattern}.feature")
|
126
|
+
end
|
127
|
+
|
128
|
+
if except.any?
|
129
|
+
except.each do |f|
|
130
|
+
features_to_exclude = Dir.glob("#{@options[:feature_path]}/#{f}.feature")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
(features_to_include - features_to_exclude).uniq
|
135
|
+
end # collect_features_by_glob
|
136
|
+
end # class Parser
|
137
|
+
end # module Cellophane
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'cellophane'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cellophane
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Phillip Koebbe
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-09 00:00:00 -06:00
|
18
|
+
default_executable: cellophane
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Cellophane is a thin wrapper around Cucumber, making it easier to be creative when running features.
|
22
|
+
email: phillip@livingdoor.net
|
23
|
+
executables:
|
24
|
+
- cellophane
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- LICENSE.txt
|
29
|
+
- README.textile
|
30
|
+
files:
|
31
|
+
- .cellophane.yaml
|
32
|
+
- .cellophane.yaml.sample
|
33
|
+
- .document
|
34
|
+
- LICENSE.txt
|
35
|
+
- README.textile
|
36
|
+
- Rakefile
|
37
|
+
- VERSION
|
38
|
+
- bin/cellophane
|
39
|
+
- features/feature_paths.feature
|
40
|
+
- features/glob_pattern.feature
|
41
|
+
- features/project_options.feature
|
42
|
+
- features/regular_expression_pattern.feature
|
43
|
+
- features/step_definitions.feature
|
44
|
+
- features/support/cellophane_methods.rb
|
45
|
+
- features/support/cellophane_steps.rb
|
46
|
+
- features/support/env.rb
|
47
|
+
- features/tags.feature
|
48
|
+
- lib/cellophane/main.rb
|
49
|
+
- lib/cellophane/options.rb
|
50
|
+
- lib/cellophane/parser.rb
|
51
|
+
- spec/cellophane_spec.rb
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/phillipkoebbe/cellophane
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: A thin wrapper around Cucumber.
|
85
|
+
test_files:
|
86
|
+
- spec/cellophane_spec.rb
|
87
|
+
- spec/spec_helper.rb
|