features 0.1.4 → 0.2.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.

Potentially problematic release.


This version of features might be problematic. Click here for more details.

Files changed (77) hide show
  1. data/Manifest +59 -0
  2. data/README.markdown +31 -1
  3. data/Rakefile +1 -1
  4. data/features.gemspec +3 -3
  5. data/generators/features/templates/run_features.rb +27 -0
  6. data/lib/ObjC/objc_feature.rb +4 -27
  7. data/lib/ObjC/objc_scenario.rb +1 -58
  8. data/lib/ObjC/objc_step.rb +0 -31
  9. data/lib/ObjC/objc_suite.rb +14 -122
  10. data/lib/Rails/rails_feature.rb +7 -30
  11. data/lib/Rails/rails_scenario.rb +0 -57
  12. data/lib/Rails/rails_step.rb +1 -30
  13. data/lib/Rails/rails_suite.rb +16 -132
  14. data/lib/feature.rb +29 -1
  15. data/lib/scenario.rb +99 -0
  16. data/lib/step.rb +28 -0
  17. data/lib/suite.rb +124 -0
  18. data/website/README +10 -0
  19. data/website/Rakefile +44 -0
  20. data/website/config.yaml +18 -0
  21. data/website/deploy.rb +29 -0
  22. data/website/deploy/1251795725/features/browserfix.css +7 -0
  23. data/website/deploy/1251795725/features/default.css +257 -0
  24. data/website/deploy/1251795725/features/images/bodybg.gif +0 -0
  25. data/website/deploy/1251795725/features/images/header-bg.gif +0 -0
  26. data/website/deploy/1251795725/features/images/logo.png +0 -0
  27. data/website/deploy/1251795725/features/images/minus.png +0 -0
  28. data/website/deploy/1251795725/features/images/plus.png +0 -0
  29. data/website/deploy/1251795725/features/images/sidebarbg.gif +0 -0
  30. data/website/deploy/1251795725/features/index.html +229 -0
  31. data/website/deploy/1251796894/features/browserfix.css +7 -0
  32. data/website/deploy/1251796894/features/default.css +257 -0
  33. data/website/deploy/1251796894/features/images/bodybg.gif +0 -0
  34. data/website/deploy/1251796894/features/images/header-bg.gif +0 -0
  35. data/website/deploy/1251796894/features/images/logo.png +0 -0
  36. data/website/deploy/1251796894/features/images/minus.png +0 -0
  37. data/website/deploy/1251796894/features/images/output-screen-1.png +0 -0
  38. data/website/deploy/1251796894/features/images/plus.png +0 -0
  39. data/website/deploy/1251796894/features/images/sidebarbg.gif +0 -0
  40. data/website/deploy/1251796894/features/index.html +229 -0
  41. data/website/deploy/1251822430/features/browserfix.css +7 -0
  42. data/website/deploy/1251822430/features/default.css +257 -0
  43. data/website/deploy/1251822430/features/images/bodybg.gif +0 -0
  44. data/website/deploy/1251822430/features/images/header-bg.gif +0 -0
  45. data/website/deploy/1251822430/features/images/logo.png +0 -0
  46. data/website/deploy/1251822430/features/images/minus.png +0 -0
  47. data/website/deploy/1251822430/features/images/output-screen-1.png +0 -0
  48. data/website/deploy/1251822430/features/images/plus.png +0 -0
  49. data/website/deploy/1251822430/features/images/sidebarbg.gif +0 -0
  50. data/website/deploy/1251822430/features/index.html +229 -0
  51. data/website/ext/init.rb +10 -0
  52. data/website/out/browserfix.css +7 -0
  53. data/website/out/default.css +257 -0
  54. data/website/out/images/bodybg.gif +0 -0
  55. data/website/out/images/header-bg.gif +0 -0
  56. data/website/out/images/logo.png +0 -0
  57. data/website/out/images/minus.png +0 -0
  58. data/website/out/images/output-screen-1.png +0 -0
  59. data/website/out/images/plus.png +0 -0
  60. data/website/out/images/sidebarbg.gif +0 -0
  61. data/website/out/index.html +229 -0
  62. data/website/src/browserfix.css +7 -0
  63. data/website/src/default.css +257 -0
  64. data/website/src/default.template +92 -0
  65. data/website/src/images/bodybg.gif +0 -0
  66. data/website/src/images/deploy.rb +29 -0
  67. data/website/src/images/header-bg.gif +0 -0
  68. data/website/src/images/logo.png +0 -0
  69. data/website/src/images/minus.png +0 -0
  70. data/website/src/images/output-screen-1.png +0 -0
  71. data/website/src/images/plus.png +0 -0
  72. data/website/src/images/sidebarbg.gif +0 -0
  73. data/website/src/images/webgen.rb +3 -0
  74. data/website/src/index.page +139 -0
  75. data/website/webgen.cache +0 -0
  76. data/website/webgen.rb +3 -0
  77. metadata +61 -2
@@ -1,36 +1,13 @@
1
1
  class RailsFeature < Feature
2
- attr_reader :scenarios, :scenario_keyword,
3
- :title, :body, :parent, :parser,
4
- :given_scenario_keyword, :keyword
5
-
6
- def initialize(hash={})
7
- @title = hash[:title]
8
- @body = hash[:body]
9
- @parent = hash[:parent]
10
- @keyword = hash[:keyword] || "Feature:"
11
- @scenario_keyword = hash[:scenario_keyword] || "Scenario:"
12
- @given_scenario_keyword = hash[:given_scenario_keyword] || "GivenScenario:"
13
-
14
- raise "No title given" unless title
15
- raise "No body given" unless body
16
- end
17
-
18
- def story
19
- body.split(/#{scenario_keyword}/)[0].split(/#{keyword}\s#{title}/).join(" ").strip
20
- end
21
-
22
- def story_html
23
- story.split("\n").join(" <br />")
24
- end
25
-
26
- def parse_scenarios
2
+ def parse_scenarios(klass=RailsScenario)
27
3
  title_body_arr = Parser.title_and_body_by_keyword_from_string({
28
4
  :string => body,
29
5
  :keyword => scenario_keyword
30
6
  })
31
- @scenarios = title_body_arr.map {|hash| RailsScenario.new(hash.update({
7
+ @scenarios = title_body_arr.map {|hash| klass.new(hash.update({
32
8
  :parent => self,
33
- :given_scenario_keyword => given_scenario_keyword
9
+ :given_scenario_keyword => given_scenario_keyword,
10
+ :follow_up_keyword => follow_up_keyword
34
11
  }))}
35
12
  @scenarios.each {|scenario| scenario.collect_steps}
36
13
  self
@@ -38,9 +15,9 @@ class RailsFeature < Feature
38
15
 
39
16
  def to_s
40
17
  <<-END
41
- class #{test_case_name} < FeaturesTestCaseClass
42
- #{scenarios.map {|s| s.to_s}.join(" ")}
43
- end
18
+ class #{test_case_name} < FeaturesTestCaseClass
19
+ #{scenarios.map {|s| s.to_s}.join("\n")}
20
+ end
44
21
  END
45
22
  end
46
23
 
@@ -1,34 +1,4 @@
1
1
  class RailsScenario < Scenario
2
- attr_reader :steps, :lines, :given_scenario_keyword,
3
- :title, :body, :parent, :passed
4
-
5
- def initialize(hash)
6
- @title = hash[:title]
7
- @body = hash[:body]
8
- @parent = hash[:parent]
9
- @given_scenario_keyword = hash[:given_scenario_keyword] || "GivenScenario:"
10
-
11
- raise "No title given" unless title
12
- raise "No body given" unless body
13
- end
14
-
15
- def keyword
16
- parent.scenario_keyword
17
- end
18
-
19
- def to_html
20
- <<-END
21
- <div class="scenario #{passed? ? "passed" : "failed"}">
22
- <h2 class="scenario_title">#{keyword} #{title}</h2>
23
- #{steps.map {|s| s.to_html}.join(" \n")}
24
- </div>
25
- END
26
- end
27
-
28
- def passed?
29
- !!@passed
30
- end
31
-
32
2
  def verify_status(results="")
33
3
  test_case_name = parent.test_case_name
34
4
 
@@ -40,37 +10,10 @@ class RailsScenario < Scenario
40
10
  @passed = result ? false : true
41
11
  end
42
12
 
43
- def collect_steps
44
-
45
- if has_given_scenarios?
46
- @body = expand_given_scenarios_in_body.strip
47
- end
48
-
49
-
50
- @lines = body.split(/\n+/).map {|s| s.strip}
51
- raise "No Steps found" if lines.empty?
52
-
53
- @steps = parse_lines
54
- self
55
- end
56
-
57
- def expand_given_scenarios_in_body
58
- raise "No associated Feature" unless parent
59
- raise "Associated Feature has no Scenarios" unless parent.scenarios
60
- body.gsub(/#{given_scenario_keyword}(.*)/) do |m|
61
- existing_scenario = parent.scenarios.detect {|s| s.title == $1.strip }
62
- existing_scenario ? existing_scenario.body.strip : ""
63
- end
64
- end
65
-
66
13
  def parse_lines
67
14
  lines.map {|l| RailsStep.new({:body => l}).aggregate!}
68
15
  end
69
16
 
70
- def has_given_scenarios?
71
- !!(body =~ /#{given_scenario_keyword}(.*)/)
72
- end
73
-
74
17
  def to_s
75
18
  <<-END
76
19
  def #{test_name}
@@ -1,39 +1,10 @@
1
1
  class RailsStep < Step
2
- attr_reader :message
3
-
4
- attr_reader :body, :parent
5
- def initialize(hash)
6
- @body = hash[:body]
7
- @parent = hash[:parent]
8
- raise "No body given" unless body
9
- end
10
-
11
- def to_html
12
- s = <<-END
13
- <h3 class="step">#{body}</h3>
14
- END
15
- s.strip
16
- end
17
-
18
- def aggregate!
19
- @message = first_part + args_string
20
- self
21
- end
22
-
23
2
  def first_part
24
3
  body.gsub(/\s+/,"_").gsub(/'[^']*'/, "__").remove_invalid_chars.sub(/./) do |first_char|
25
4
  first_char.downcase
26
5
  end
27
6
  end
28
-
29
- def has_args?
30
- !args.empty?
31
- end
32
-
33
- def args
34
- @args ||= body.scan(/'([^']*)'/).map {|a| a[0]}
35
- end
36
-
7
+
37
8
  def args_string
38
9
  if has_args?
39
10
  "(" + args.map {|a| '"' + a.to_s + '"'}.join(", ") + ")"
@@ -4,7 +4,8 @@ class RailsSuite < Suite
4
4
  :features, :test_cases_file,
5
5
  :feature_keyword, :scenario_keyword,
6
6
  :project_name, :passed,
7
- :given_scenario_keyword, :features_helper
7
+ :given_scenario_keyword, :features_helper,
8
+ :follow_up_keyword
8
9
 
9
10
  def initialize(hash)
10
11
  @feature_files_path = hash[:feature_files_path]
@@ -15,147 +16,30 @@ class RailsSuite < Suite
15
16
  @scenario_keyword = hash[:scenario_keyword] || "Scenario:"
16
17
  @given_scenario_keyword = hash[:given_scenario_keyword] || "GivenScenario:"
17
18
  @project_name = hash[:project_name] || "Features"
19
+ @follow_up_keyword = hash[:follow_up_keyword] || "And"
18
20
  @feature_files = all_feature_files
19
21
  @feature_files_as_strings = all_feature_files_as_strings
20
22
  end
21
23
 
22
- def parse_output_file_and_open_in_browser(file)
23
- results = ""
24
- File.open(file) do |f|
25
- f.readlines.each do |l|
26
- results << l
27
- end
28
- end
29
-
30
- html = parse_results(results).html
31
-
32
- %x(touch '/tmp/out.html' && echo '#{html}' > /tmp/out.html && open '/tmp/out.html' )
33
-
34
- end
35
-
36
- def parse_results_and_open_in_safari(results)
37
- html = parse_results(results).html
38
- open_in_safari(html)
39
- end
40
-
41
- def open_in_safari(html)
42
- %x(touch '/tmp/out.html' && echo '#{html}' > /tmp/out.html && open '/tmp/out.html' )
43
- end
44
-
45
-
46
- def parse_results(results="")
47
- parse
48
- @passed = true
49
- features.each do |f|
50
- f.scenarios.each do |s|
51
- s.verify_status(results)
52
- @passed &&= s.passed?
53
- end
54
- end
55
- self
56
- end
57
-
58
- def passed?
59
- @passed
60
- end
61
-
62
- def html
63
- <<-END
64
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
65
- <html>
66
- <head>
67
- <style type="text/css">
68
- body {
69
- font-family: Lucida Grande;
70
- background-color: #FEFEFE;
71
- padding: 5px;
72
- }
73
- * {-webkit-border-radius: 10px;}
74
- body {background-color: #EEE; -webkit-border-radius: 0px;}
75
- .feature { background-color: #E0E0E0;padding: 20px; margin: 20px;}
76
- .scenario { background-color: #EEE; padding: 20px; margin: 20px}
77
- .failed {border: 1px solid #C88; background-color: #B77; }
78
- .passed {border: 1px solid #8C8; background-color: #7B7; }
79
- .feature_title {font-size: 24pt; }
80
- .story { font-size: 18pt; padding-left: 40px;}
81
- #indicator { width: 100%; height: 20px; }
82
- #wrap { background-color: #EEE; padding: 20px }
83
- </style></head>
84
- </head>
85
- <body>
86
- <div id="wrap">
87
- <div id="header">
88
- <div id="indicator" class="#{passed? ? "passed" : "failed"}">&nbsp;</div>
89
- <h1 class="project_name">#{project_name}</h1>
90
-
91
- </div>
92
- #{features.map {|f| f.to_html }.join(" \n")}
93
- </div>
94
- </body>
95
- </html>
96
- END
97
- end
98
-
99
- def valid?
100
- unique_feature_test_case_names?
101
- end
102
-
103
- def unique_feature_test_case_names?
104
- feature_test_case_name = features.map {|f| f.test_case_name }
105
- feature_test_case_name == feature_test_case_name.uniq
106
- end
107
-
108
- def run
109
- parse
110
- raise "Invalid: Duplicated Titles" unless valid?
111
- File.open(test_cases_file, "w") { |f| f.puts self }
112
- end
113
-
114
- def parse
115
- parse_features
116
- parse_feature_scenarios
117
- end
118
-
119
- def to_s
120
- <<-END
121
- require "#{features_helper}"
122
- #{features.map {|f| f.to_s }.join(" ")}
123
- END
124
- end
125
-
126
- def parse_features
24
+ def parse_features(klass=RailsFeature)
127
25
  title_body_arr = Parser.title_and_body_by_keyword_from_string({
128
26
  :string => feature_files_as_strings.join(" "),
129
27
  :keyword => feature_keyword
130
28
  })
131
- @features = title_body_arr.map {|hash| RailsFeature.new(hash.update({:keyword => feature_keyword, :scenario_keyword => scenario_keyword}))}
132
- end
133
-
134
- def parse_feature_scenarios
135
- features.each do |feature|
136
- feature.parse_scenarios
137
- end
138
- end
139
-
140
- def all_feature_files
141
- all_entries_in_feature_files_path = Dir.new(feature_files_path).entries
142
- feature_entries = all_entries_in_feature_files_path.select do |file|
143
- !!(file =~ /\.#{feature_file_suffix}$/)
144
- end
145
- feature_entries.map do |file|
146
- feature_files_path + "/" + file
29
+ @features = title_body_arr.map do |hash|
30
+ klass.new(hash.update({
31
+ :keyword => feature_keyword,
32
+ :scenario_keyword => scenario_keyword,
33
+ :given_scenario_keyword => given_scenario_keyword,
34
+ :follow_up_keyword => follow_up_keyword}))
147
35
  end
148
36
  end
149
37
 
150
- def all_feature_files_as_strings
151
- feature_files.map do |file|
152
- feature_string = ""
153
- File.open(file) do |f|
154
- f.readlines.each do |l|
155
- feature_string << l
156
- end
157
- end
158
- feature_string
159
- end
38
+ def to_s
39
+ <<-END
40
+ require "#{features_helper}"
41
+
42
+ #{features.map {|f| f.to_s }.join("\n")}
43
+ END
160
44
  end
161
45
  end
@@ -1,2 +1,30 @@
1
- class Feature
1
+ class Feature
2
+
3
+ attr_reader :scenarios, :scenario_keyword,
4
+ :title, :body, :parent, :parser,
5
+ :given_scenario_keyword, :keyword,
6
+ :follow_up_keyword
7
+
8
+ def initialize(hash={})
9
+ @title = hash[:title]
10
+ @body = hash[:body]
11
+ @parent = hash[:parent]
12
+ @keyword = hash[:keyword] || "Feature:"
13
+ @scenario_keyword = hash[:scenario_keyword] || "Scenario:"
14
+ @given_scenario_keyword = hash[:given_scenario_keyword] || "GivenScenario:"
15
+ @follow_up_keyword = hash[:follow_up_keyword] || "And"
16
+
17
+ raise "No title given" unless title
18
+ raise "No body given" unless body
19
+ end
20
+
21
+ def story
22
+ body.split(/#{scenario_keyword}/)[0].split(/#{keyword}\s#{title}/).join(" ").strip
23
+ end
24
+
25
+ def story_html
26
+ story.split("\n").join(" <br />")
27
+ end
28
+
29
+
2
30
  end
@@ -1,2 +1,101 @@
1
1
  class Scenario
2
+ attr_reader :steps, :lines, :given_scenario_keyword,
3
+ :title, :body, :parent, :passed, :follow_up_keyword
4
+
5
+ def initialize(hash)
6
+ @title = hash[:title]
7
+ @body = hash[:body]
8
+ @parent = hash[:parent]
9
+ @given_scenario_keyword = hash[:given_scenario_keyword] || "GivenScenario:"
10
+ @follow_up_keyword = hash[:follow_up_keyword] || "And"
11
+
12
+ raise "No title given" unless title
13
+ raise "No body given" unless body
14
+ end
15
+
16
+ def keyword
17
+ parent.scenario_keyword
18
+ end
19
+
20
+ def to_html
21
+ <<-END
22
+ <div class="scenario #{passed? ? "passed" : "failed"}">
23
+ <h2 class="scenario_title">#{keyword} #{title}</h2>
24
+ #{steps.map {|s| s.to_html}.join(" \n")}
25
+ </div>
26
+ END
27
+ end
28
+
29
+ def passed?
30
+ !!@passed
31
+ end
32
+
33
+ def collect_steps
34
+ if has_given_scenarios?
35
+ @body = expand_given_scenarios_in_body.strip
36
+ end
37
+
38
+ if has_follow_ups?
39
+ @body = expand_follow_ups_in_body.strip
40
+ end
41
+
42
+
43
+ @lines = body.split(/\n+/).map {|s| s.strip}
44
+ raise "No Steps found" if lines.empty?
45
+
46
+ @steps = parse_lines
47
+ self
48
+ end
49
+
50
+ def expand_given_scenarios_in_body
51
+ raise "No associated Feature" unless parent
52
+ raise "Associated Feature has no Scenarios" unless parent.scenarios
53
+ body.gsub(/#{given_scenario_keyword}(.*)/) do |m|
54
+ existing_scenario = parent.scenarios.detect {|s| s.title == $1.strip }
55
+ existing_scenario ? existing_scenario.body.strip : ""
56
+ end
57
+ end
58
+
59
+ def expand_follow_ups_in_body
60
+ lines = body.split("\n")
61
+ new_lines = []
62
+ step = ""
63
+ for line in lines
64
+ if line =~ follow_up_line_exp
65
+ args = line.sub(/^\s*#{follow_up_keyword}\s*/, "").split("|").reject {|arg| arg =~ /\A\s*\Z/ }.map {|arg| "'" + arg + "'"}
66
+ arr = step.split("'").reject {|elt| elt =~ /\A\s*\Z/ }
67
+ offset = (step =~ /\A\s*'/) ? 0 : 1
68
+ new_step = ""
69
+
70
+ arr.each_with_index do |elt, index|
71
+ if index % 2 == offset
72
+ new_step << args[(index - offset) / 2]
73
+ else
74
+ new_step << elt
75
+ end
76
+ end
77
+ new_lines << new_step
78
+ else
79
+ new_lines << line
80
+ step = line
81
+ end
82
+ end
83
+ new_lines.join("\n")
84
+ end
85
+
86
+ def given_scenario_line_exp
87
+ /#{given_scenario_keyword}(.*)/
88
+ end
89
+
90
+ def has_given_scenarios?
91
+ !!(body =~ given_scenario_line_exp)
92
+ end
93
+
94
+ def follow_up_line_exp
95
+ /#{follow_up_keyword}\s*\|(.*)\s*\|\s*/
96
+ end
97
+
98
+ def has_follow_ups?
99
+ !!(body =~ follow_up_line_exp)
100
+ end
2
101
  end