specdown 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ ## CHANGELOG
2
+
3
+ ## 0.1.0 (2012/01/01)
4
+
5
+ Better Sandboxing of tests, with support for configuring the expectation/assertion framework (RSpec Expectations and Test::Unit asserstions supported)
6
+
7
+ See the "Setting up your test environment" section in the README for more information (https://github.com/moonmaster9000/specdown/blob/ccd9aea13173d582241ca39e6f4f10d50fab4490/README.markdown).
8
+
9
+ ## 0.0.1 (2011/12/30)
10
+
11
+ First release! Barely passable! :-)
data/README.markdown CHANGED
@@ -1 +1,171 @@
1
- This library has not yet been implemented. See SPEC.markdown for info about what it might become.
1
+ # specdown
2
+
3
+ Write your README in markdown, and execute it with specdown.
4
+
5
+ ## Why?
6
+
7
+ Simply put, `specdown` takes README DRIVEN DEVELOPMENT one step further by making your markdown executable.
8
+
9
+ If you don't know what README DRIVEN DEVELOPMENT IS, checkout Tom Preston Werner's blog post ["README Driven Development"](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html)
10
+
11
+ ## CAVEAT
12
+
13
+ This library has just been released. It has only a few features, but expect those to grow rapidly over the coming weeks. Follow this repo to stay up to date on the latest changes, or better yet, fork and implement some needed features (see the TODO list at the end of this README).
14
+
15
+ ## Installation
16
+
17
+ Right now, specdown only support's Ruby. Next, I'll write a javascript implementation. Then, I don't know what language. Regardless, the goal is that you could use specdown with any programming language you desire.
18
+
19
+ To install the `specdown` ruby gem, simply:
20
+
21
+ $ gem install specdown
22
+
23
+ It comes with a `specdown` command. Try running it. Doesn't matter where.
24
+
25
+ ## Usage
26
+
27
+ Let's write a simple test in markdown, and execute it with specdown. Create a "specdown" directory, then save the following text into a file inside of it. I'll assume you're calling it "example.markdown":
28
+
29
+ ```markdown
30
+ # Our first test!
31
+
32
+ This is our very first test. It's going to blow your mind.
33
+
34
+ raise "WTF?" unless 1 == 1
35
+ ```
36
+
37
+ Ok, if you've been following along, then `ls -R` should return the following directory structure:
38
+
39
+ $ ls -R
40
+
41
+ specdown/
42
+ example.markdown
43
+
44
+ Great. Now run the `specdown` command:
45
+
46
+ $ specdown
47
+
48
+ .
49
+
50
+ 1 markdown
51
+ 1 test
52
+ 0 failures
53
+
54
+ Booya!
55
+
56
+ ### How does it work?
57
+
58
+ `specdown` loads any "markdown" files it can find inside the "specdown" directory, parses them into trees, then performs exhaustive depth-first searches on the trees to execute the code.
59
+
60
+ Let's update our README to help illustrate this:
61
+
62
+ ```markdown
63
+ # Our first test!
64
+
65
+ This is our very first test. It's going to blow your mind.
66
+
67
+ raise "WTF?" unless 1 == 1
68
+
69
+ ## A Subsection
70
+
71
+ In this section, we're going to create a variable.
72
+
73
+ name = "moonmaster9000"
74
+
75
+ ### A sub subsection
76
+
77
+ In this subsection, we have access to anything created or within scope in parent sections:
78
+
79
+ raise "name not in scope" if !defined? name
80
+
81
+ ## Another Subsection
82
+
83
+ In this subsection, we don't have access to the "name" variable. Think of your markdown as a tree.
84
+
85
+ raise "name in scope" if defined? name
86
+ ```
87
+
88
+ Read through that. I'm giving you some important scoping hints in it.
89
+
90
+ Save it, run it.
91
+
92
+ $ specdown
93
+
94
+ ..
95
+
96
+ 1 markdown
97
+ 2 tests
98
+ 0 failures
99
+
100
+ Notice how the headers in your markdown form a tree?
101
+
102
+ #Our first test!
103
+ / \
104
+ / \
105
+ / \
106
+ / \
107
+ / \
108
+ / \
109
+ / \
110
+ ##A Subection ##Another Subsection
111
+ /
112
+ /
113
+ /
114
+ ###A sub subsection
115
+
116
+ Specdown turned that tree into two tests. The first test (#Our first test! --> ##A Subsection --> ###A sub subsection):
117
+
118
+ ```ruby
119
+ raise "WTF?" unless 1 == 1
120
+ name = "moonmaster9000"
121
+ raise "name not in scope" if !defined? name
122
+ ```
123
+
124
+ Here's what the second test looked like (#Our first test! --> ##Another Subsection)
125
+
126
+ ```ruby
127
+ raise "WTF?" unless 1 == 1
128
+ raise "name in scope" if defined? name
129
+ ```
130
+
131
+ ## Setting up your test environment
132
+
133
+ Similar to the cucumber testing framework: If you put a ruby file somewhere inside your "specdown" directory, `specdown` will find it and load it.
134
+
135
+ ### Configuring the Expectation / Assertion framework
136
+
137
+ As of version 0.1.0, `specdown` supports both RSpec expectations and Test::Unit assertions.
138
+
139
+ Specdown will default to RSpec expectations, but if it can't find the "rspec" gem installed on your system, it will fall back to Test::Unit assertions.
140
+
141
+ You can also configure `Specdown` manually to use RSpec expectations or Test::Unit assertions.
142
+
143
+ #### RSpec expectations
144
+
145
+ Create a "support" directory inside your specdown directory, and add an `env.rb` file containing the following Ruby code:
146
+
147
+ ```ruby
148
+ Specdown::Config.expectations = :rspec
149
+ ```
150
+
151
+ You can now use [RSpec expectations](https://www.relishapp.com/rspec/rspec-expectations) in your tests.
152
+
153
+ #### Using Test::Unit::Assertions
154
+
155
+ Create a "specdown/support/env.rb" file in your app, then add the following to it:
156
+
157
+ ```ruby
158
+ Specdown::Config.expectations = :test_unit
159
+ ```
160
+
161
+ You can now use [Test::Unit::Assertions](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/test/unit/rdoc/Test/Unit/Assertions.html) inside your tests.
162
+
163
+ ## TODO
164
+
165
+ This library is the result of about 8 hours worth of work so far :-) It's a basic minimum viable product, but there are tons of features I want to implement. Here's what's on my immediate horizon:
166
+
167
+ * Better stack traces / reporting
168
+ * Test hooks (before/after/around)
169
+ * Run a single test
170
+ * color code the terminal output
171
+ * offer the option of outputing the actual markdown while it executes, instead of "..F....FF......"
@@ -18,76 +18,79 @@ Feature: `specdown` command
18
18
 
19
19
  Given I have a specdown directory containing a single markdown file:
20
20
  """
21
- # Specdown Example
21
+ # Specdown Example
22
22
 
23
- This is an example specdown file.
23
+ This is an example specdown file.
24
24
 
25
- ## Child Node
25
+ ## Child Node
26
26
 
27
- This section is a child node. It contains some ruby code:
28
-
29
- "simple code".should_not == nil
27
+ This section is a child node. It contains some ruby code:
28
+
29
+ "simple code".should_not be(nil)
30
30
 
31
- ### First Leaf
31
+ ### First Leaf
32
32
 
33
- This section has a failure simulation:
34
-
35
- raise "specdown error simulation!"
33
+ This section has a failure simulation:
34
+
35
+ raise "specdown error simulation!"
36
36
 
37
- ## Last Leaf
37
+ ## Last Leaf
38
38
 
39
- This section is a leaf node. It contains some ruby code:
40
-
41
- 1.should == 1
39
+ This section is a leaf node. It contains some ruby code:
40
+
41
+ 1.should satisfy(&:odd?)
42
42
  """
43
43
 
44
44
  When I run `specdown` with no arguments
45
45
  Then I should see the following output:
46
46
  """
47
+ F.
48
+
47
49
  1 markdown
48
50
  2 tests
49
- 2 failures
51
+ 1 failure
50
52
 
51
- undefined method `should_not' for "simple code":String
52
- undefined method `should' for 1:Fixnum
53
+ specdown error simulation
53
54
  """
54
55
 
55
56
  Scenario: `specdown` with no arguments, and a specdown directory containing a single ruby file and a single markdown file
56
57
  Given I have a specdown directory containing a markdown file:
57
58
  """
58
- # Specdown Example
59
+ # Specdown Example
59
60
 
60
- This is an example specdown file.
61
+ This is an example specdown file.
61
62
 
62
- ## Child Node
63
+ ## Child Node
63
64
 
64
- This section is a child node. It contains some ruby code:
65
-
66
- "simple code".should_not == nil
65
+ This section is a child node. It contains some ruby code:
66
+
67
+ "simple code".should_not be(nil)
67
68
 
68
- ### First Leaf
69
+ ### First Leaf
69
70
 
70
- This section has a failure simulation:
71
-
72
- raise "specdown error simulation!"
71
+ This section has a failure simulation:
72
+
73
+ raise "specdown error simulation!"
73
74
 
74
- ## Last Leaf
75
+ ## Last Leaf
75
76
 
76
- This section is a leaf node. It contains some ruby code:
77
-
78
- 1.should == 1
77
+ This section is a leaf node. It contains some ruby code:
78
+
79
+ 1.should satisfy(&:odd?)
79
80
  """
80
81
  And a single ruby file:
81
82
  """
82
- require 'rubygems'
83
- require 'rspec/expectations'
83
+ Specdown::Config.expectations = :test_unit
84
84
  """
85
85
  When I run `specdown` with no arguments
86
86
  Then I should see the following output:
87
87
  """
88
+ FF
89
+
88
90
  1 markdown
89
91
  2 tests
90
- 1 failure
92
+ 2 failures
91
93
 
92
- specdown error simulation!
94
+ In parser_example.markdown: <NoMethodError> undefined method `be'
95
+ In parser_example.markdown: <NoMethodError> undefined method `satisfy'
93
96
  """
@@ -0,0 +1,96 @@
1
+ Feature: Specdown::Config
2
+
3
+ You can use a simple Ruby API to configure specdown.
4
+
5
+ Note that at any point, you can reset your Specdown::Config to defaults using the `reset!` method:
6
+
7
+ Specdown::Config.reset!
8
+ Specdown::Config.expectations.should be(nil)
9
+
10
+ \## Assertion/Expectation framework
11
+
12
+ All of your tests will run within a protected sandbox namespace. If rspec is installed and available, then Specdown will make rspec `should` expectations and matchers availble to your tests. If not, then it will fall back to Test::Unit assertions.
13
+
14
+ \### Rspec Expectations
15
+
16
+ If you want to explicitly configure `Specdown` to use rspec's expectations, you can use the `expectations` accessor:
17
+
18
+ Specdown::Config.expectations = :rspec
19
+
20
+ Now, if you instantiate a new sandbox, then rspec's "should" matchers should be available within it:
21
+
22
+ Specdown.sandbox.new.instance_eval do
23
+ 1.should_not be(nil)
24
+ end
25
+
26
+ \### Test::Unit assertions
27
+
28
+ If you want to explicitly configure `Specdown` to use Test::Unit assertions, set the `expectations` accessor on `Specdown::Config` to `:test_unit`:
29
+
30
+ Specdown::Config.expectations = :test_unit
31
+
32
+ Now you'll have access to all of the `assert_*` methods within your tests:
33
+
34
+ Specdown.sandbox.new.instance_eval do
35
+ assert_equal 1, 1
36
+ end
37
+
38
+
39
+ Scenario: Reset the Specdown::Config
40
+
41
+ Given I have configured Specdown:
42
+ """
43
+ Specdown::Config.expectations = :rspec
44
+ """
45
+
46
+ When I reset Specdown:
47
+ """
48
+ Specdown::Config.reset!
49
+ """
50
+
51
+ Then my specdown configuration should return to it's defaults:
52
+ """
53
+ Specdown::Config.expectations.should be(nil)
54
+ """
55
+
56
+
57
+ Scenario: Default to Rspec expectations
58
+
59
+ Given I have rspec installed
60
+ Then Specdown should provide rspec expectations and matchers within the sandbox by default:
61
+ """
62
+ Specdown.sandbox.instance_eval do
63
+ 1.should_not be(nil)
64
+ end
65
+ """
66
+
67
+ Scenario: Manually configure RSpec expectations
68
+ Given I have manually configured Specdown to use Rspec expectations:
69
+ """
70
+ Specdown::Config.expectations = :rspec
71
+ """
72
+
73
+ Then Specdown should provide rspec expectations and matchers within the sandbox:
74
+ """
75
+ Specdown.sandbox.instance_eval do
76
+ 1.should_not be(nil)
77
+ end
78
+ """
79
+
80
+ Scenario: Manually configure Test::Unit assertions
81
+ Given I have manually configured Specdown to use Test::Unit assertions:
82
+ """
83
+ Specdown::Config.expectations = :test_unit
84
+ """
85
+
86
+ Then Specdown should provide Test::Unit assertions within the sandbox:
87
+ """
88
+ Specdown.sandbox.instance_eval do
89
+ assert_equal 1, 1
90
+ end
91
+ """
92
+
93
+
94
+
95
+
96
+
@@ -0,0 +1,39 @@
1
+ Feature: Specdown::EventServer
2
+
3
+ The Specdown::EventServer receives events and triggers callbacks.
4
+
5
+ For example, to send an event to the EventServer, simply give the event a name, then pass any number of arguments after that:
6
+
7
+ Specdown::EventServer.event "test passed"
8
+
9
+ You can also register callbacks for events using the `register` method:
10
+
11
+ Specdown::EventServer.register "test passed" do
12
+ print "."
13
+ end
14
+
15
+
16
+ Scenario: Submitting an event triggers all registered callbacks for that event
17
+
18
+ Given a USA population of zero:
19
+ """
20
+ @usa_population = 0
21
+ """
22
+ And a world population of 10:
23
+ """
24
+ @world_population = 10
25
+ """
26
+ And I register callbacks to increment both whenever someone is born:
27
+ """
28
+ Specdown::EventServer.register("birth") {|num_births| @usa_population += num_births }
29
+ Specdown::EventServer.register("birth") {|num_births| @world_population += num_births }
30
+ """
31
+ When I create a "birth" event:
32
+ """
33
+ Specdown::EventServer.event "birth", 3
34
+ """
35
+ Then both the USA population and the World population should increase:
36
+ """
37
+ @usa_population.should == 3
38
+ @world_population.should == 13
39
+ """
@@ -6,7 +6,7 @@ This is an example specdown file.
6
6
 
7
7
  This section is a child node. It contains some ruby code:
8
8
 
9
- "simple code".should_not == nil
9
+ "simple code".should_not be(nil)
10
10
 
11
11
  ### First Leaf
12
12
 
@@ -18,4 +18,4 @@ This section has a failure simulation:
18
18
 
19
19
  This section is a leaf node. It contains some ruby code:
20
20
 
21
- 1.should == 1
21
+ 1.should satisfy(&:odd?)
@@ -6,27 +6,27 @@ Feature: Specdown Parser
6
6
 
7
7
  readme = <<-README
8
8
 
9
- \# Specdown Example
9
+ \# Specdown Example
10
10
 
11
- This is an example specdown file.
11
+ This is an example specdown file.
12
12
 
13
- \## Child Node
13
+ \## Child Node
14
14
 
15
- This section is a child node. It contains some ruby code:
16
-
17
- "simple code".should_not == nil
15
+ This section is a child node. It contains some ruby code:
16
+
17
+ "simple code".should_not be(nil)
18
18
 
19
- \### First Leaf
19
+ \### First Leaf
20
20
 
21
- This section has a failure simulation:
22
-
23
- raise "specdown error simulation!"
21
+ This section has a failure simulation:
22
+
23
+ raise "specdown error simulation!"
24
24
 
25
- \## Last Leaf
25
+ \## Last Leaf
26
26
 
27
- This section is a leaf node. It contains some ruby code:
28
-
29
- 1.should == 1
27
+ This section is a leaf node. It contains some ruby code:
28
+
29
+ 1.should satisfy(&:odd?)
30
30
  README
31
31
 
32
32
  As you can see, this forms a tree, with "# Specdown Example" at the root of the tree, and "## Leaf 1" and "## Leaf 2" as the children / leafs.
@@ -55,27 +55,27 @@ Feature: Specdown Parser
55
55
 
56
56
  Given the following specdown example file:
57
57
  """
58
- # Specdown Example
58
+ # Specdown Example
59
59
 
60
- This is an example specdown file.
60
+ This is an example specdown file.
61
61
 
62
- ## Child Node
62
+ ## Child Node
63
63
 
64
- This section is a child node. It contains some ruby code:
65
-
66
- "simple code".should_not == nil
64
+ This section is a child node. It contains some ruby code:
65
+
66
+ "simple code".should_not be(nil)
67
67
 
68
- ### First Leaf
68
+ ### First Leaf
69
69
 
70
- This section has a failure simulation:
71
-
72
- raise "specdown error simulation!"
70
+ This section has a failure simulation:
71
+
72
+ raise "specdown error simulation!"
73
73
 
74
- ## Last Leaf
74
+ ## Last Leaf
75
75
 
76
- This section is a leaf node. It contains some ruby code:
77
-
78
- 1.should == 1
76
+ This section is a leaf node. It contains some ruby code:
77
+
78
+ 1.should satisfy(&:odd?)
79
79
  """
80
80
 
81
81
  When I parse it into a tree:
@@ -98,11 +98,11 @@ Feature: Specdown Parser
98
98
  last_leaf = @tree.root.children.last
99
99
 
100
100
  child_node.name.should == "Child Node"
101
- child_node.code.should == '"simple code".should_not == nil'
101
+ child_node.code.should == '"simple code".should_not be(nil)'
102
102
 
103
103
  first_leaf.name.should == "First Leaf"
104
104
  first_leaf.code.should == 'raise "specdown error simulation!"'
105
105
 
106
106
  last_leaf.name.should == "Last Leaf"
107
- last_leaf.code.should == "1.should == 1"
107
+ last_leaf.code.should == "1.should satisfy(&:odd?)"
108
108
  """
@@ -2,7 +2,7 @@ Feature: Runner
2
2
 
3
3
  The `Specdown::Runner` class accepts a markdown parse tree, and runs the tests found within.
4
4
 
5
- Imagine we start with this markdown file:
5
+ Imagine we start with this markdown file (saved at "specdown.markdown"):
6
6
 
7
7
  \# Specdown Example
8
8
 
@@ -26,16 +26,12 @@ Feature: Runner
26
26
 
27
27
  1.should == 1
28
28
 
29
- We then parse it into a Specdown::Tree:
30
-
31
- parse_tree = Specdown::Parser.parse File.read("/path/to/markdown")
32
-
33
- We can now generate a new `Specdown::Runner` instance and run the tests:
29
+ We can generate a `Specdown::Runner` instance and run the tests in our markdown by simply passing the filename on instantiation:
34
30
 
35
- runner = Specdown::Runner.new(parse_tree)
31
+ runner = Specdown::Runner.new "specdown.markdown"
36
32
  runner.run
37
33
 
38
- While running, it will print results to STDOUT like "F."
34
+ While running, it will emit events during the processing to the Specdown::EventServer. This enables all kinds of functionality, like printing the progress of the tests.
39
35
 
40
36
  We can access statistics about the run programatically:
41
37
 
@@ -46,7 +42,7 @@ Feature: Runner
46
42
 
47
43
  Scenario: Running tests
48
44
 
49
- Given the following specdown example file:
45
+ Given the following specdown example file located at 'features/fixtures/parser_example.markdown':
50
46
  """
51
47
  # Specdown Example
52
48
 
@@ -71,14 +67,9 @@ Feature: Runner
71
67
  1.should == 1
72
68
  """
73
69
 
74
- When I parse it into a tree:
75
- """
76
- @tree = Specdown::Parser.parse @readme
77
- """
78
-
79
- And I generate a `Specdown::Runner` instance from it:
70
+ When I generate a `Specdown::Runner` instance from it:
80
71
  """
81
- @runner = Specdown::Runner.new @tree
72
+ @runner = Specdown::Runner.new "features/fixtures/parser_example.markdown"
82
73
  """
83
74
 
84
75
  Then I should be able to run the tests:
@@ -88,8 +79,9 @@ Feature: Runner
88
79
 
89
80
  And I should be able to access the report data programatically:
90
81
  """
91
- @runner.stats.tests #==> 2
92
- @runner.stats.failures #==> 1
93
- @runner.stats.successes #==> 1
94
- @runner.stats.exceptions.map(&:to_s) #==> ['(eval):3:in `execute_test': specdown error simulation!']
82
+ @runner.file_name.should == 'parser_example.markdown'
83
+ @runner.stats.tests.should == 2
84
+ @runner.stats.failures.should == 1
85
+ @runner.stats.successes.should == 1
86
+ @runner.stats.exceptions.map(&:to_s).should == ["specdown error simulation!"]
95
87
  """
@@ -6,7 +6,7 @@ This is an example specdown file.
6
6
 
7
7
  This section is a child node. It contains some ruby code:
8
8
 
9
- "simple code".should_not == nil
9
+ "simple code".should_not be(nil)
10
10
 
11
11
  ### First Leaf
12
12
 
@@ -18,4 +18,5 @@ This section has a failure simulation:
18
18
 
19
19
  This section is a leaf node. It contains some ruby code:
20
20
 
21
- 1.should == 1
21
+ 1.should satisfy(&:odd?)
22
+
@@ -1,2 +1 @@
1
- require 'rubygems'
2
- require 'rspec/expectations'
1
+ Specdown::Config.expectations = :test_unit
@@ -6,7 +6,7 @@ This is an example specdown file.
6
6
 
7
7
  This section is a child node. It contains some ruby code:
8
8
 
9
- "simple code".should_not == nil
9
+ "simple code".should_not be(nil)
10
10
 
11
11
  ### First Leaf
12
12
 
@@ -18,4 +18,5 @@ This section has a failure simulation:
18
18
 
19
19
  This section is a leaf node. It contains some ruby code:
20
20
 
21
- 1.should == 1
21
+ 1.should satisfy(&:odd?)
22
+
@@ -0,0 +1,34 @@
1
+ Given /^I have configured Specdown:$/ do |string|
2
+ eval string
3
+ end
4
+
5
+ When /^I reset Specdown:$/ do |string|
6
+ eval string
7
+ end
8
+
9
+ Then /^my specdown configuration should return to it's defaults:$/ do |string|
10
+ eval string
11
+ end
12
+
13
+ Given /^I have rspec installed$/ do
14
+ end
15
+
16
+ Then /^Specdown should provide rspec expectations and matchers within the sandbox by default:$/ do |string|
17
+ eval string
18
+ end
19
+
20
+ Given /^I have manually configured Specdown to use Rspec expectations:$/ do |string|
21
+ eval string
22
+ end
23
+
24
+ Then /^Specdown should provide rspec expectations and matchers within the sandbox:$/ do |string|
25
+ eval string
26
+ end
27
+
28
+ Given /^I have manually configured Specdown to use Test::Unit assertions:$/ do |string|
29
+ eval string
30
+ end
31
+
32
+ Then /^Specdown should provide Test::Unit assertions within the sandbox:$/ do |string|
33
+ eval string
34
+ end
@@ -0,0 +1,19 @@
1
+ Given /^a USA population of zero:$/ do |string|
2
+ eval string
3
+ end
4
+
5
+ Given /^a world population of .*:$/ do |string|
6
+ eval string
7
+ end
8
+
9
+ Given /^I register callbacks to increment both whenever someone is born:$/ do |string|
10
+ eval string
11
+ end
12
+
13
+ When /^I create a ".*" event:$/ do |string|
14
+ eval string
15
+ end
16
+
17
+ Then /^both the USA population and the World population should increase:$/ do |string|
18
+ eval string
19
+ end
@@ -1,4 +1,4 @@
1
- Given /^the following specdown example file:$/ do |string|
1
+ Given /^the following specdown example file.*:$/ do |string|
2
2
  @readme = File.read "features/fixtures/parser_example.markdown"
3
3
  end
4
4
 
@@ -1,4 +1,3 @@
1
1
  $LOAD_PATH.unshift './lib'
2
2
  require 'specdown'
3
3
  require 'rspec/expectations'
4
- require 'rspec/matchers'
@@ -0,0 +1,3 @@
1
+ Before do
2
+ Specdown::Config.reset!
3
+ end
@@ -11,14 +11,7 @@ module Specdown
11
11
 
12
12
  private
13
13
  def run
14
- @results =
15
- @markdowns.map {|markdown|
16
- Parser.parse(File.read(markdown))
17
- }.map {|tree|
18
- Runner.new(tree)
19
- }.map {|runner|
20
- runner.run
21
- }.collect &:stats
14
+ @results = @markdowns.map {|markdown| Runner.new(markdown).run.stats}
22
15
  end
23
16
 
24
17
  def report
@@ -0,0 +1,11 @@
1
+ module Specdown
2
+ module Config
3
+ extend self
4
+
5
+ attr_accessor :expectations
6
+
7
+ def reset!
8
+ self.expectations = nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ Specdown::EventServer.register :run_complete do
2
+ puts "\n\n"
3
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ Specdown::EventServer.register :test_failed do
2
+ print "F"
3
+ end
@@ -0,0 +1,3 @@
1
+ Specdown::EventServer.register :test_passed do
2
+ print "."
3
+ end
@@ -0,0 +1,19 @@
1
+ module Specdown
2
+ module EventServer
3
+ extend self
4
+
5
+ def event(event_name, *args)
6
+ callbacks[event_name].map {|callback| callback.call *args} if callbacks[event_name]
7
+ end
8
+
9
+ def register(event_name, &callback)
10
+ callbacks[event_name] ||= []
11
+ callbacks[event_name] << callback
12
+ end
13
+
14
+ private
15
+ def callbacks
16
+ @callbacks ||= {}
17
+ end
18
+ end
19
+ end
@@ -53,6 +53,7 @@ module Specdown
53
53
  until parsed_elements.empty? || (
54
54
  parsed_elements.first.type == :header && parsed_elements.first.options[:level] == 1
55
55
  )
56
+ parsed_elements.shift
56
57
  end
57
58
  end
58
59
  end
@@ -34,7 +34,16 @@ module Specdown
34
34
  end
35
35
 
36
36
  def exceptions
37
- @stats.collect(&:exceptions).flatten.map {|e| [e.to_s, e.backtrace].join "\n"}
37
+ formatted_exceptions = []
38
+ @stats.each do |stat|
39
+ formatted_exceptions << stat.exceptions.map do |e|
40
+ [
41
+ (stat.runner ? "In #{stat.runner.file_name}: " : "") + "<#{e.class}> #{e}",
42
+ e.backtrace
43
+ ].join "\n"
44
+ end
45
+ end
46
+ formatted_exceptions.flatten
38
47
  end
39
48
  end
40
49
  end
@@ -1,8 +1,10 @@
1
1
  module Specdown
2
2
  class Stats
3
3
  attr_accessor :tests, :exceptions
4
+ attr_reader :runner
4
5
 
5
- def initialize
6
+ def initialize(source_runner=nil)
7
+ @runner = source_runner
6
8
  @tests = 0
7
9
  @exceptions = []
8
10
  end
@@ -1,15 +1,20 @@
1
1
  module Specdown
2
2
  class Runner
3
- attr_reader :stats
3
+ attr_reader :stats, :file_path
4
4
 
5
- def initialize(tree)
6
- @tree = tree
7
- @stats = Stats.new
5
+ def initialize(file_path)
6
+ @file_path = file_path
7
+ @tree = Parser.parse File.read(file_path)
8
+ @stats = Stats.new self
9
+ end
10
+
11
+ def file_name
12
+ File.basename @file_path
8
13
  end
9
14
 
10
15
  def run
11
16
  depth_first_search @tree.root
12
- puts "\n\n"
17
+ EventServer.event :run_complete
13
18
  self
14
19
  end
15
20
 
@@ -28,16 +33,17 @@ module Specdown
28
33
  @stats.tests += 1
29
34
 
30
35
  begin
31
- Sandbox.new.instance_eval do
32
- eval code.join("\n")
33
- end
36
+ Specdown.sandbox.instance_eval <<-CODE, file_name
37
+ #{code.join("\n")}
38
+ CODE
34
39
 
35
- print '.'
40
+ EventServer.event :test_passed
36
41
 
37
42
  rescue Exception => e
38
43
  @stats.exceptions << e
39
44
 
40
- print 'F'
45
+ EventServer.event :test_failed
46
+
41
47
  end
42
48
  end
43
49
  end
@@ -0,0 +1,56 @@
1
+ module Specdown
2
+ module SandboxFactory
3
+ extend self
4
+
5
+ def generate
6
+ Module.new {}.tap do |sandbox|
7
+ setup_expectation_library.call(sandbox)
8
+ end
9
+ end
10
+
11
+ private
12
+ def setup_expectation_library
13
+ expectation_library_setups[Config.expectations]
14
+ end
15
+
16
+ def expectation_library_setups
17
+ Hash.new(default_expectations_setup).tap do |setups|
18
+ setups[:rspec] = rspec_expectations_setup
19
+ setups[:test_unit] = test_unit_expectations_setup
20
+ end
21
+ end
22
+
23
+ def rspec_installed?
24
+ require_rspec
25
+ end
26
+
27
+ def require_rspec
28
+ begin
29
+ require('rspec/expectations')
30
+ true
31
+ rescue LoadError
32
+ false
33
+ end
34
+ end
35
+
36
+ def default_expectations_setup
37
+ if rspec_installed?
38
+ rspec_expectations_setup
39
+ else
40
+ test_unit_expectations_setup
41
+ end
42
+ end
43
+
44
+ def rspec_expectations_setup
45
+ require_rspec
46
+ proc {|sandbox| sandbox.extend ::RSpec::Matchers}
47
+ end
48
+
49
+ def test_unit_expectations_setup
50
+ proc do |sandbox|
51
+ require 'test/unit/assertions'
52
+ sandbox.extend ::Test::Unit::Assertions
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ module Specdown
2
+ extend self
3
+
4
+ def sandbox
5
+ SandboxFactory.generate
6
+ end
7
+ end
data/lib/specdown.rb CHANGED
@@ -2,8 +2,15 @@ require 'kramdown'
2
2
  require 'specdown/parser'
3
3
  require 'specdown/node'
4
4
  require 'specdown/tree'
5
- require 'specdown/sandbox'
6
5
  require 'specdown/runner'
7
6
  require 'specdown/runner/report'
8
7
  require 'specdown/runner/stats'
9
8
  require 'specdown/command'
9
+ require 'specdown/event_server'
10
+ require 'specdown/event_handlers/run_complete'
11
+ require 'specdown/event_handlers/run_started'
12
+ require 'specdown/event_handlers/test_failed'
13
+ require 'specdown/event_handlers/test_passed'
14
+ require 'specdown/config'
15
+ require 'specdown/specdown'
16
+ require 'specdown/sandbox_factory'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specdown
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
8
  - 1
10
- version: 0.0.1
9
+ - 0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Parker
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-30 00:00:00 Z
18
+ date: 2012-01-01 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: kramdown
@@ -68,22 +68,31 @@ executables:
68
68
  extensions: []
69
69
 
70
70
  extra_rdoc_files:
71
+ - CHANGELOG.markdown
71
72
  - README.markdown
72
- - SPEC.markdown
73
73
  files:
74
74
  - lib/specdown/command.rb
75
+ - lib/specdown/config.rb
76
+ - lib/specdown/event_handlers/run_complete.rb
77
+ - lib/specdown/event_handlers/run_started.rb
78
+ - lib/specdown/event_handlers/test_failed.rb
79
+ - lib/specdown/event_handlers/test_passed.rb
80
+ - lib/specdown/event_server.rb
75
81
  - lib/specdown/node.rb
76
82
  - lib/specdown/parser.rb
77
83
  - lib/specdown/runner/report.rb
78
84
  - lib/specdown/runner/stats.rb
79
85
  - lib/specdown/runner.rb
80
- - lib/specdown/sandbox.rb
86
+ - lib/specdown/sandbox_factory.rb
87
+ - lib/specdown/specdown.rb
81
88
  - lib/specdown/tree.rb
82
89
  - lib/specdown.rb
83
90
  - bin/specdown
91
+ - CHANGELOG.markdown
84
92
  - README.markdown
85
- - SPEC.markdown
86
93
  - features/command.feature
94
+ - features/config.feature
95
+ - features/event_server.feature
87
96
  - features/fixtures/parser_example.markdown
88
97
  - features/parser.feature
89
98
  - features/report.feature
@@ -92,10 +101,13 @@ files:
92
101
  - features/specdown_examples/with_ruby/specdown/env.rb
93
102
  - features/specdown_examples/with_ruby/specdown/parser_example.markdown
94
103
  - features/step_definitions/command.rb
104
+ - features/step_definitions/config.rb
105
+ - features/step_definitions/event_server.rb
95
106
  - features/step_definitions/parser.rb
96
107
  - features/step_definitions/report.rb
97
108
  - features/step_definitions/runner.rb
98
109
  - features/support/env.rb
110
+ - features/support/hooks.rb
99
111
  homepage: http://github.com/moonmaster9000/specdown
100
112
  licenses: []
101
113
 
@@ -131,6 +143,8 @@ specification_version: 3
131
143
  summary: Write your specs as if they were a README, then EXECUTE them.
132
144
  test_files:
133
145
  - features/command.feature
146
+ - features/config.feature
147
+ - features/event_server.feature
134
148
  - features/fixtures/parser_example.markdown
135
149
  - features/parser.feature
136
150
  - features/report.feature
@@ -139,7 +153,10 @@ test_files:
139
153
  - features/specdown_examples/with_ruby/specdown/env.rb
140
154
  - features/specdown_examples/with_ruby/specdown/parser_example.markdown
141
155
  - features/step_definitions/command.rb
156
+ - features/step_definitions/config.rb
157
+ - features/step_definitions/event_server.rb
142
158
  - features/step_definitions/parser.rb
143
159
  - features/step_definitions/report.rb
144
160
  - features/step_definitions/runner.rb
145
161
  - features/support/env.rb
162
+ - features/support/hooks.rb
data/SPEC.markdown DELETED
@@ -1,122 +0,0 @@
1
- # specdown
2
-
3
- Write your README in markdown, and execute it with specdown. Documentation == Specification == WINNING
4
-
5
- ## Why?
6
-
7
- I love README DRIVEN DEVELOPMENT. I love the free-form nature of writing a README. I dislike the process of copying and transforming my README into cucumber. Gherkin works well for many types of requirements, but I dislike turning readable, natural prose into "Given/When/Then" Gherkin. I dislike turning my readable prose into RSpec even more.
8
-
9
- ## How?
10
-
11
- Writing your tests with specdown is as simple as writing a README. I'll show you.
12
-
13
- Let's imagine we're writing a README for a silly little ruby library called "Todo":
14
-
15
- # Todo
16
-
17
- A simple library for managing your todo list.
18
-
19
- ## Usage
20
-
21
- Go into `irb`, then `require 'todo'`. Type `Todo.List` to see your current to do list:
22
-
23
- Todo.List #==> []
24
-
25
- If at any point, you want to completely reset your Todo.List, simply call `Todo!`:
26
-
27
- Todo!
28
- Todo.List.should be_empty
29
-
30
- ### Adding/Removing
31
-
32
- Now, add an item to your to-do list by calling it as if it were a method on the `Todo` object:
33
-
34
- Todo.shop_for_groceries
35
-
36
- This added the item to your `Todo.List`:
37
-
38
- Todo.List.should == [:shop_for_groceries]
39
-
40
- You can remove an item from your todo list by adding an exclamation point onto the end of it:
41
- Todo.shop_for_groceries!
42
- Todo.List.should be_empty
43
-
44
-
45
- ### Bulk operations
46
-
47
- You can also perform bulk operations on your `Todo.List`:
48
-
49
- Todo.edit! do
50
- shop
51
- dry_cleaning!
52
- shave!
53
- walk_dogs
54
- end
55
-
56
- The items "dry_cleaning" and "shave" were removed from your `Todo.List`, and the items "shop" and "walk_dogs" were added to your Todo.List.
57
-
58
- Todo.List.should == [
59
- :shop,
60
- :walk_dogs
61
- ]
62
-
63
- Let's call this file "README.markdown", and place it inside a "specdown/" directory:
64
-
65
- $ ls -r
66
- specdown/
67
- README.markdown
68
-
69
- We can execute this markdown with specdown by simply the `specdown` command. If you'd like to have some code run before the markdown is executed, put it in a ruby file inside a "specdown" directory:
70
-
71
- $ cat > specdown/support.rb
72
-
73
- $LOAD_PATH.unshift './lib'
74
- require 'rspec/expectations' # for using "should" matchers in the README
75
- require 'todo'
76
-
77
- ## Trees and Leafs
78
-
79
- In the README that we wrote for `Todo`, we actually wrote two tests, or scenarios.
80
-
81
- `specdown` creates a tree out of our markdown. In our case, the tree for our `Todo` README looks like this:
82
-
83
-
84
- #Todo
85
- |
86
- ##Usage
87
- / \
88
- / \
89
- / \
90
- / \
91
- / \
92
- / \
93
- / \
94
- ###Adding/Removing ###Bulk Operations
95
-
96
- `specdown` will then walk the path from the root to every leaf node in the tree, executing any code it finds along the way.
97
-
98
- Thus, the first "test" will look like this:
99
-
100
- Todo.List #==> []
101
- Todo!
102
- Todo.List.should be_empty
103
- Todo.shop_for_groceries
104
- Todo.List.should == [:shop_for_groceries]
105
- Todo.shop_for_groceries!
106
- Todo.List.should be_empty
107
-
108
- The second "test" looks like this:
109
-
110
- Todo.List
111
- Todo!
112
- Todo.List.should be_empty
113
- Todo.edit! do
114
- shop
115
- dry_cleaning!
116
- shave!
117
- walk_dogs
118
- end
119
- Todo.List.should == [
120
- :shop,
121
- :walk_dogs
122
- ]
@@ -1,4 +0,0 @@
1
- module Specdown
2
- class Sandbox
3
- end
4
- end