specdown 0.3.0 → 0.4.0.beta.1

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.
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