specdown 0.3.0 → 0.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGELOG.markdown +7 -0
  2. data/README.markdown +84 -8
  3. data/VERSION +1 -1
  4. data/features/command.feature +3 -1
  5. data/features/config.feature +3 -3
  6. data/features/fixtures/codeblocks_and_implicits.markdown +7 -0
  7. data/features/implicit_parser.feature +113 -0
  8. data/features/implicit_specs.feature +135 -0
  9. data/features/parser.feature +69 -1
  10. data/features/pending_specs.feature +49 -0
  11. data/features/report_summary.feature +1 -1
  12. data/features/specdown_examples/complete_implicit/specdown/implicit.specdown +4 -0
  13. data/features/specdown_examples/complete_implicit/specdown/test.markdown +3 -0
  14. data/features/specdown_examples/no_implicit/specdown/test.markdown +3 -0
  15. data/features/specdown_examples/pending_implicit/specdown/implicit.specdown +4 -0
  16. data/features/specdown_examples/pending_implicit/specdown/test.markdown +3 -0
  17. data/features/specdown_examples/pending_specs/specdown/test.markdown +7 -0
  18. data/features/step_definitions/command.rb +7 -3
  19. data/features/step_definitions/implicit_parser.rb +19 -0
  20. data/features/step_definitions/implicit_specs.rb +18 -0
  21. data/features/step_definitions/parser.rb +24 -0
  22. data/features/step_definitions/pending_specs.rb +7 -0
  23. data/features/support/env.rb +6 -0
  24. data/lib/specdown.rb +6 -0
  25. data/lib/specdown/config.rb +12 -1
  26. data/lib/specdown/event_handlers/test_pending.rb +3 -0
  27. data/lib/specdown/event_handlers/test_undefined.rb +3 -0
  28. data/lib/specdown/implicit_parser.rb +47 -0
  29. data/lib/specdown/node.rb +2 -1
  30. data/lib/specdown/parser.rb +40 -6
  31. data/lib/specdown/pending.rb +7 -0
  32. data/lib/specdown/pending_exception.rb +4 -0
  33. data/lib/specdown/reporter.rb +8 -0
  34. data/lib/specdown/reporters/color_terminal_reporter.rb +12 -0
  35. data/lib/specdown/reporters/terminal_reporter.rb +16 -0
  36. data/lib/specdown/reporters/text_reporter.rb +8 -0
  37. data/lib/specdown/runner.rb +25 -16
  38. data/lib/specdown/runner/report_summary.rb +8 -0
  39. data/lib/specdown/runner/stats.rb +13 -2
  40. data/lib/specdown/sandbox_decorators/pending.rb +3 -0
  41. data/lib/specdown/templates/color_summary.erb +20 -4
  42. data/lib/specdown/templates/summary.erb +17 -0
  43. metadata +45 -9
@@ -171,7 +171,6 @@ Feature: Specdown Parser
171
171
  """
172
172
 
173
173
 
174
- @focus
175
174
  Scenario: Multiple code blocks in a section should join together with newlines
176
175
 
177
176
  Given the following specdown example file containing multiple executable codeblocks in a single section:
@@ -198,3 +197,72 @@ Feature: Specdown Parser
198
197
  """
199
198
  @tree.root.code.should == "hi = 'hello'\nputs hi"
200
199
  """
200
+
201
+ @focus
202
+ Scenario: Code blocks + Undefined Implicit Specs
203
+
204
+ Given the following README:
205
+ """
206
+ @readme = <<-README.undent
207
+ # Specdown Example
208
+
209
+ **This is an implicit spec.**
210
+
211
+ This text has two implicit specs: **one** and **two**.
212
+
213
+ ```ruby
214
+ hi = "explicit spec"
215
+ ```
216
+ README
217
+ """
218
+
219
+ When I parse it into a tree:
220
+ """
221
+ @tree = Specdown::Parser.parse @readme
222
+ """
223
+
224
+ Then the root node should include the explicit code:
225
+ """
226
+ @tree.root.code.should == %{hi = "explicit spec"}
227
+ """
228
+
229
+ And the root node should include the undefined implicit specs:
230
+ """
231
+ @tree.root.undefined_implicits.should == ["This is an implicit spec.", "one", "two"]
232
+ """
233
+
234
+ @focus
235
+ Scenario: Code blocks + Defined Implicit Specs
236
+
237
+ Given the following README:
238
+ """
239
+ @readme = <<-README.undent
240
+ # Specdown Example
241
+
242
+ **This is an implicit spec.**
243
+
244
+ ```ruby
245
+ hi = "explicit spec"
246
+ ```
247
+ README
248
+ """
249
+
250
+ And the following implicit specs:
251
+ """
252
+ @implicits = <<-SPECDOWN.undent
253
+ This is an implicit spec.
254
+ -----------------------------
255
+
256
+ puts "howdy"
257
+ SPECDOWN
258
+ """
259
+
260
+ When I parse it into a tree:
261
+ """
262
+ @tree = Specdown::Parser.parse @readme, Specdown::ImplicitParser.parse(@implicits)
263
+ """
264
+
265
+ Then the code block and the implicit spec should be joined together:
266
+ """
267
+ @tree.root.code.should == %{puts "howdy"\nhi = "explicit spec"}
268
+ """
@@ -0,0 +1,49 @@
1
+ Feature: Pending specs
2
+
3
+ You can mark a spec as pending by using the "pending" method.
4
+
5
+ For example, consider the following markdown file:
6
+
7
+ Example of Pending Specification
8
+ -----------------------------------
9
+
10
+ This spec is pending.
11
+
12
+ pending
13
+
14
+
15
+ If you ran this with specdown, you'd receive the following output:
16
+
17
+
18
+ P
19
+
20
+ 1 markdown
21
+ 1 test
22
+ 1 pending
23
+ 0 successes
24
+ 0 failures
25
+
26
+
27
+ Scenario: Pending specification
28
+
29
+ Given the following markdown with a pending spec:
30
+ """
31
+ # Example of Pending Specification
32
+
33
+ This spec is pending.
34
+
35
+ ```ruby
36
+ pending
37
+ ```
38
+ """
39
+
40
+ Then the `specdown` command should return the following output:
41
+ """
42
+ P
43
+
44
+ 1 markdown
45
+ 1 test
46
+ 1 pending
47
+ 0 successes
48
+ 0 failures
49
+ """
@@ -12,7 +12,7 @@ Feature: Specdown::ReportSummary
12
12
 
13
13
  Scenario: A Specdown::Reporter instantiated with a single stats object
14
14
 
15
- Given the following specdown example file located at 'features/fixtures/parser_example.markdown':
15
+ Given the following specdown example file:
16
16
  """
17
17
  # Specdown Example
18
18
 
@@ -0,0 +1,4 @@
1
+ An implicit spec.
2
+ --------------------------
3
+
4
+ raise "oops!" unless 1 == 1
@@ -0,0 +1,3 @@
1
+ # Heading
2
+
3
+ **An implicit spec.**
@@ -0,0 +1,3 @@
1
+ # Heading
2
+
3
+ **An implicit spec.**
@@ -0,0 +1,4 @@
1
+ An implicit spec.
2
+ --------------------------
3
+
4
+ pending
@@ -0,0 +1,3 @@
1
+ # Heading
2
+
3
+ **An implicit spec.**
@@ -0,0 +1,7 @@
1
+ # Example of Pending Specification
2
+
3
+ This spec is pending.
4
+
5
+ ```ruby
6
+ pending
7
+ ```
@@ -3,9 +3,7 @@ When /^I run `specdown` from the command line in a directory that contains no 's
3
3
  end
4
4
 
5
5
  Then /^I should see the following output:$/ do |string|
6
- string.split("\n").each do |line|
7
- @output.should include(line.strip)
8
- end
6
+ ensure_included! string, @output
9
7
  end
10
8
 
11
9
  Given /^I have a specdown directory containing a (?:single )?markdown file:$/ do |string|
@@ -106,6 +104,12 @@ Given /^`specdown \-\-format=condensed`$/ do
106
104
  ensure_condensed! bundle_exec!("specdown --format=condensed")
107
105
  end
108
106
 
107
+ def ensure_included!(desired, actual)
108
+ desired.split("\n").each do |line|
109
+ actual.should include(line.strip)
110
+ end
111
+ end
112
+
109
113
  def ensure_condensed!(output)
110
114
  output.strip.should_not be_empty
111
115
  output.should match(/^[^\. ]+\.markdown: .*$/)
@@ -0,0 +1,19 @@
1
+ Given /^the following implicit specification:$/ do |string|
2
+ eval string
3
+ end
4
+
5
+ When /^I parse it with the implicit parser:$/ do |string|
6
+ eval string
7
+ end
8
+
9
+ Then /^I should receive a hash lookup of implicit definitions:$/ do |string|
10
+ eval string
11
+ end
12
+
13
+ Given /^two implicit specification strings:$/ do |string|
14
+ eval string
15
+ end
16
+
17
+ When /^I pass both off to the Specdown::ImplicitParser:$/ do |string|
18
+ eval string
19
+ end
@@ -0,0 +1,18 @@
1
+ Given /^a markdown file with an implicit spec:$/ do |string|
2
+ end
3
+
4
+ Given /^no implicit specification$/ do
5
+ @directory = "features/specdown_examples/no_implicit/"
6
+ end
7
+
8
+ When /^I run the `specdown` command$/ do
9
+ @output = bundle_exec! "specdown"
10
+ end
11
+
12
+ Given /^a specdown file with a pending specification:$/ do |string|
13
+ @directory = "features/specdown_examples/pending_implicit/"
14
+ end
15
+
16
+ Given /^a specdown file with a complete specification:$/ do |string|
17
+ @directory = "features/specdown_examples/complete_implicit/"
18
+ end
@@ -10,6 +10,30 @@ Given /^the following specdown example file containing multiple executable codeb
10
10
  @readme = File.read "features/fixtures/multiple_codeblocks_per_section_example.markdown"
11
11
  end
12
12
 
13
+ Given /^the following specdown example file containing both codeblocks and implicit specs:$/ do |string|
14
+ @readme = File.read "features/fixtures/codeblocks_and_implicits.markdown"
15
+ end
16
+
17
+ Given /^the following README:$/ do |string|
18
+ eval string
19
+ end
20
+
21
+ Then /^the root node should include the explicit code:$/ do |string|
22
+ eval string
23
+ end
24
+
25
+ Then /^the root node should include the undefined implicit specs:$/ do |string|
26
+ eval string
27
+ end
28
+
29
+ Given /^the following implicit specs:$/ do |string|
30
+ eval string
31
+ end
32
+
33
+ Then /^the code block and the implicit spec should be joined together:$/ do |string|
34
+ eval string
35
+ end
36
+
13
37
  When /^I parse it into a tree:$/ do |string|
14
38
  eval string
15
39
  end
@@ -0,0 +1,7 @@
1
+ Given /^the following markdown with a pending spec:$/ do |string|
2
+ @directory = "features/specdown_examples/pending_specs/"
3
+ end
4
+
5
+ Then /^the `specdown` command should return the following output:$/ do |string|
6
+ ensure_included! string, bundle_exec!("specdown")
7
+ end
@@ -1,3 +1,9 @@
1
1
  $LOAD_PATH.unshift './lib'
2
2
  require 'specdown'
3
3
  require 'rspec/expectations'
4
+
5
+ class String
6
+ def undent
7
+ gsub(/^.{#{slice(/^ +/).length}}/, '')
8
+ end
9
+ end
data/lib/specdown.rb CHANGED
@@ -3,6 +3,7 @@ require 'term/ansicolor'
3
3
  require 'erb'
4
4
  require 'optparse'
5
5
  require 'specdown/parser'
6
+ require 'specdown/implicit_parser'
6
7
  require 'specdown/node'
7
8
  require 'specdown/tree'
8
9
  require 'specdown/runner'
@@ -17,6 +18,10 @@ require 'specdown/event_handlers/run_complete'
17
18
  require 'specdown/event_handlers/run_started'
18
19
  require 'specdown/event_handlers/test_failed'
19
20
  require 'specdown/event_handlers/test_passed'
21
+ require 'specdown/event_handlers/test_pending'
22
+ require 'specdown/event_handlers/test_undefined'
23
+ require 'specdown/pending'
24
+ require 'specdown/pending_exception'
20
25
  require 'specdown/config'
21
26
  require 'specdown/specdown'
22
27
  require 'specdown/sandbox_factory'
@@ -31,3 +36,4 @@ require 'specdown/reporters/text_reporter'
31
36
  require 'specdown/sandbox_decorators/default_assertion_library'
32
37
  require 'specdown/sandbox_decorators/rspec_expectations'
33
38
  require 'specdown/sandbox_decorators/test_unit_assertions'
39
+ require 'specdown/sandbox_decorators/pending'
@@ -9,7 +9,7 @@ module Specdown
9
9
  attr_accessor :format
10
10
 
11
11
  def format
12
- @format ||= :short
12
+ @format ||= :condensed
13
13
  end
14
14
 
15
15
  def reporter
@@ -35,6 +35,12 @@ module Specdown
35
35
  @tests ||= find_tests_in root
36
36
  end
37
37
 
38
+ def implicit_specs
39
+ return @implicit_specs if @implicit_specs
40
+ implicit_spec_files = find_implicit_specs_in(root).map {|file| File.read(file)}
41
+ @implicit_specs = Specdown::ImplicitParser.parse *implicit_spec_files
42
+ end
43
+
38
44
  def tests=(test_files)
39
45
  unless test_files.empty?
40
46
  @tests = test_files
@@ -66,6 +72,11 @@ module Specdown
66
72
  Dir["#{directory}/**/*.markdown"] + Dir["#{directory}/**/*.md"]
67
73
  end
68
74
 
75
+ def find_implicit_specs_in(directory)
76
+ directory = strip_trailing_slash directory
77
+ Dir["#{directory}/**/*.specdown"]
78
+ end
79
+
69
80
  def strip_trailing_slash(string)
70
81
  if string[-1..-1] == "/"
71
82
  string[0...-1]
@@ -0,0 +1,3 @@
1
+ Specdown::EventServer.register :test_pending do
2
+ Specdown.reporter.print_pending nil
3
+ end
@@ -0,0 +1,3 @@
1
+ Specdown::EventServer.register :test_undefined do |test|
2
+ Specdown.reporter.print_undefined nil
3
+ end
@@ -0,0 +1,47 @@
1
+ module Specdown
2
+ class ImplicitParser
3
+ def self.parse(*implicit_specifications)
4
+ self.new(*implicit_specifications).parse
5
+ end
6
+
7
+ def initialize(*implicit_specifications)
8
+ @specs = implicit_specifications
9
+ @lookups = {}
10
+ end
11
+
12
+ def parse
13
+ @specs.each do |spec|
14
+ kramdown = Kramdown::Document.new spec, :input => "GithubMarkdown"
15
+ build_lookup kramdown.root.children
16
+ end
17
+
18
+ @lookups
19
+ end
20
+
21
+ private
22
+ def build_lookup(parsed_elements)
23
+ scan_for_header parsed_elements
24
+ consume_section parsed_elements until parsed_elements.empty?
25
+ end
26
+
27
+ def consume_section(parsed_elements)
28
+ key = parsed_elements.shift.options[:raw_text]
29
+ value = ""
30
+
31
+ while !parsed_elements.empty? && parsed_elements.first.type != :header
32
+ element = parsed_elements.shift
33
+ value += "\n" + element.value.to_s.strip if element.type == :codeblock && ([nil, "ruby", ""].include? element.options["language"])
34
+ end
35
+
36
+ @lookups[key] = value.strip
37
+ end
38
+
39
+ def scan_for_header(parsed_elements)
40
+ until parsed_elements.empty? || (
41
+ parsed_elements.first.type == :header
42
+ )
43
+ parsed_elements.shift
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/specdown/node.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  module Specdown
2
2
  class Node
3
- attr_accessor :name, :code, :contents, :children, :parent
3
+ attr_accessor :name, :code, :undefined_implicits, :contents, :children, :parent
4
4
 
5
5
  def initialize
6
6
  @code = ''
7
7
  @contents = ''
8
8
  @children = []
9
+ @undefined_implicits = []
9
10
  end
10
11
 
11
12
  def code
@@ -1,15 +1,22 @@
1
1
  module Specdown
2
- module Parser
3
- extend self
2
+ class Parser
3
+ def self.parse(readme, lookups={})
4
+ self.new(readme, lookups).parse
5
+ end
6
+
7
+ def initialize(readme, lookups={})
8
+ @readme = readme
9
+ @lookups = lookups
10
+ end
4
11
 
5
- def parse(readme)
6
- kramdown = Kramdown::Document.new readme, :input => "GithubMarkdown"
12
+ def parse
13
+ kramdown = Kramdown::Document.new @readme, :input => "GithubMarkdown"
7
14
  build_tree kramdown.root.children
8
15
  end
9
16
 
10
17
  private
11
18
  def build_tree(parsed_elements)
12
- tree = Tree.new
19
+ tree = Specdown::Tree.new
13
20
  scan_for_root_node parsed_elements
14
21
  tree.root = consume_section parsed_elements unless parsed_elements.empty?
15
22
  consume_children parsed_elements, tree.root unless parsed_elements.empty?
@@ -43,7 +50,22 @@ module Specdown
43
50
 
44
51
  while !parsed_elements.empty? && parsed_elements.first.type != :header
45
52
  element = parsed_elements.shift
46
- node.code += "\n" + element.value.to_s.strip if element.type == :codeblock && element.options["language"] == "ruby"
53
+
54
+ if element.type == :codeblock && element.options["language"] == "ruby"
55
+ node.code += "\n" + element.value.to_s.strip
56
+
57
+ elsif has_implicits?(element)
58
+ gather_implicit_keys(element).each do |key|
59
+ value = @lookups[key]
60
+
61
+ if value
62
+ node.code += "\n" + value
63
+ else
64
+ node.undefined_implicits << key
65
+ end
66
+ end
67
+ end
68
+
47
69
  node.contents += element.value.to_s + element.children.map(&:value).join
48
70
  end
49
71
  node
@@ -56,5 +78,17 @@ module Specdown
56
78
  parsed_elements.shift
57
79
  end
58
80
  end
81
+
82
+ def has_implicits?(element)
83
+ element.type == :strong || element.children.map {|child| has_implicits?(child) }.any?
84
+ end
85
+
86
+ def gather_implicit_keys(element)
87
+ if element.type == :strong
88
+ [element.children.map(&:value).compact.join(" ")]
89
+ else
90
+ element.children.map {|child| gather_implicit_keys(child)}.flatten
91
+ end
92
+ end
59
93
  end
60
94
  end