smartdown 0.0.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 (70) hide show
  1. data/LICENSE.md +21 -0
  2. data/README.md +204 -0
  3. data/bin/smartdown +41 -0
  4. data/lib/smartdown/engine/errors.rb +5 -0
  5. data/lib/smartdown/engine/predicate_evaluator.rb +23 -0
  6. data/lib/smartdown/engine/state.rb +63 -0
  7. data/lib/smartdown/engine/transition.rb +65 -0
  8. data/lib/smartdown/engine.rb +33 -0
  9. data/lib/smartdown/model/element/markdown_heading.rb +7 -0
  10. data/lib/smartdown/model/element/markdown_paragraph.rb +7 -0
  11. data/lib/smartdown/model/element/multiple_choice.rb +7 -0
  12. data/lib/smartdown/model/element/start_button.rb +7 -0
  13. data/lib/smartdown/model/flow.rb +24 -0
  14. data/lib/smartdown/model/front_matter.rb +37 -0
  15. data/lib/smartdown/model/nested_rule.rb +5 -0
  16. data/lib/smartdown/model/next_node_rules.rb +5 -0
  17. data/lib/smartdown/model/node.rb +47 -0
  18. data/lib/smartdown/model/predicate/equality.rb +7 -0
  19. data/lib/smartdown/model/predicate/named.rb +7 -0
  20. data/lib/smartdown/model/predicate/set_membership.rb +7 -0
  21. data/lib/smartdown/model/rule.rb +5 -0
  22. data/lib/smartdown/parser/base.rb +35 -0
  23. data/lib/smartdown/parser/directory_input.rb +61 -0
  24. data/lib/smartdown/parser/element/front_matter.rb +17 -0
  25. data/lib/smartdown/parser/element/markdown_heading.rb +14 -0
  26. data/lib/smartdown/parser/element/markdown_paragraph.rb +19 -0
  27. data/lib/smartdown/parser/element/multiple_choice_question.rb +24 -0
  28. data/lib/smartdown/parser/element/start_button.rb +15 -0
  29. data/lib/smartdown/parser/flow_interpreter.rb +50 -0
  30. data/lib/smartdown/parser/node_interpreter.rb +29 -0
  31. data/lib/smartdown/parser/node_parser.rb +37 -0
  32. data/lib/smartdown/parser/node_transform.rb +83 -0
  33. data/lib/smartdown/parser/predicates.rb +36 -0
  34. data/lib/smartdown/parser/rules.rb +51 -0
  35. data/lib/smartdown/version.rb +3 -0
  36. data/lib/smartdown.rb +9 -0
  37. data/spec/acceptance/parsing_spec.rb +109 -0
  38. data/spec/acceptance/smartdown_cli_spec.rb +16 -0
  39. data/spec/engine/predicate_evaluator_spec.rb +98 -0
  40. data/spec/engine/state_spec.rb +106 -0
  41. data/spec/engine/transition_spec.rb +150 -0
  42. data/spec/engine_spec.rb +79 -0
  43. data/spec/fixtures/acceptance/cover-sheet/cover-sheet.txt +14 -0
  44. data/spec/fixtures/acceptance/one-question/one-question.txt +3 -0
  45. data/spec/fixtures/acceptance/one-question/questions/q1.txt +9 -0
  46. data/spec/fixtures/acceptance/question-and-outcome/outcomes/o1.txt +3 -0
  47. data/spec/fixtures/acceptance/question-and-outcome/question-and-outcome.txt +3 -0
  48. data/spec/fixtures/acceptance/question-and-outcome/questions/q1.txt +9 -0
  49. data/spec/fixtures/directory_input/cover-sheet.txt +1 -0
  50. data/spec/fixtures/directory_input/outcomes/o1.txt +1 -0
  51. data/spec/fixtures/directory_input/questions/q1.txt +1 -0
  52. data/spec/fixtures/directory_input/scenarios/s1.txt +1 -0
  53. data/spec/fixtures/example.sd +17 -0
  54. data/spec/model/flow_spec.rb +42 -0
  55. data/spec/model/node_spec.rb +32 -0
  56. data/spec/parser/base_spec.rb +49 -0
  57. data/spec/parser/directory_input_spec.rb +56 -0
  58. data/spec/parser/element/front_matter_spec.rb +21 -0
  59. data/spec/parser/element/markdown_heading_spec.rb +24 -0
  60. data/spec/parser/element/markdown_paragraph_spec.rb +28 -0
  61. data/spec/parser/element/multiple_choice_question_spec.rb +31 -0
  62. data/spec/parser/element/start_button_parser_spec.rb +30 -0
  63. data/spec/parser/integration/cover_sheet_spec.rb +30 -0
  64. data/spec/parser/node_parser_spec.rb +133 -0
  65. data/spec/parser/predicates_spec.rb +65 -0
  66. data/spec/parser/rules_spec.rb +244 -0
  67. data/spec/spec_helper.rb +27 -0
  68. data/spec/support/model_builder.rb +83 -0
  69. data/spec/support_specs/model_builder_spec.rb +90 -0
  70. metadata +218 -0
@@ -0,0 +1,106 @@
1
+ require 'smartdown/engine/state'
2
+
3
+ describe Smartdown::Engine::State do
4
+ subject {
5
+ described_class.new(current_node: :start_state)
6
+ }
7
+
8
+ it "raises if current_node not given to constructor" do
9
+ expect { described_class.new() }.to raise_error(ArgumentError)
10
+ end
11
+
12
+ it "initializes path and responses" do
13
+ expect(subject.get(:responses)).to eq []
14
+ expect(subject.get(:path)).to eq []
15
+ end
16
+
17
+ describe "#get" do
18
+ it "raises if a value is undefined" do
19
+ expect { subject.get(:a) }.to raise_error(Smartdown::Engine::UndefinedValue)
20
+ end
21
+
22
+ it "by string or symbol" do
23
+ expect(subject.get(:current_node)).to eq(:start_state)
24
+ expect(subject.get("current_node")).to eq(:start_state)
25
+ end
26
+ end
27
+
28
+ describe "#keys" do
29
+ it "returns a set of all keys in the state" do
30
+ expect(subject.keys).to eq(Set.new(["current_node", "path", "responses"]))
31
+ end
32
+ end
33
+
34
+ describe "#put" do
35
+ it "returns a copy of state and leaves orignal unchanged" do
36
+ new_state = subject.put(:a, 1)
37
+ expect { subject.get(:a) }.to raise_error
38
+ expect(new_state.get(:a)).to eq 1
39
+ end
40
+
41
+ it "by string or symbol" do
42
+ s2 = subject.put(:b, 1)
43
+ expect(s2.get(:b)).to eq(1)
44
+ expect(s2.get("b")).to eq(1)
45
+ s3 = subject.put("b", 2)
46
+ expect(s3.get(:b)).to eq(2)
47
+ expect(s3.get("b")).to eq(2)
48
+ end
49
+ end
50
+
51
+ context "lambda values" do
52
+ let(:predicate) { double("predicate", call: true) }
53
+ subject(:state) {
54
+ described_class.new(current_node: :start_state, predicate?: predicate)
55
+ }
56
+
57
+ describe "#get" do
58
+ it "evaluates lambda with state" do
59
+ expect(predicate).to receive(:call).with(state).and_return(true)
60
+ expect(state.get(:predicate?)).to eq(true)
61
+ end
62
+
63
+ it "caches the result of evaluating the lambda" do
64
+ expect(predicate).to receive(:call).once
65
+ state.get(:predicate?)
66
+ state.get(:predicate?)
67
+ end
68
+ end
69
+
70
+ describe "#==" do
71
+ let(:l1) { ->(state) { true } }
72
+ let(:l2) { ->(state) { true } }
73
+
74
+ let(:state_with_l1a) { described_class.new(current_node: "red", pred: l1)}
75
+ let(:state_with_l1b) { described_class.new(current_node: "red", pred: l1)}
76
+ let(:state_with_l2) { described_class.new(current_node: "red", pred: l2)}
77
+
78
+ it "is equal if identical lambdas" do
79
+ expect(state_with_l1a).to eq(state_with_l1b)
80
+ end
81
+
82
+ it "is not equal if different lambdas" do
83
+ expect(state_with_l1a).not_to eq(state_with_l2)
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#==" do
89
+ let(:s1) { described_class.new(current_node: "red") }
90
+ let(:s2) { described_class.new(current_node: "red") }
91
+ let(:s3) { described_class.new(current_node: "green") }
92
+ let(:s4) { described_class.new(current_node: "red", a: 1) }
93
+
94
+ it "is true if two states have the same keys and values" do
95
+ expect(s1).to eq(s2)
96
+ end
97
+
98
+ it "is false if two states have the same keys with different values" do
99
+ expect(s1).not_to eq(s3)
100
+ end
101
+
102
+ it "is false if one state is a subset of the other" do
103
+ expect(s1).not_to eq(s4)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,150 @@
1
+ require 'smartdown/engine/transition'
2
+ require 'smartdown/engine/state'
3
+ require 'smartdown/model/rule'
4
+ require 'smartdown/model/nested_rule'
5
+
6
+ describe Smartdown::Engine::Transition do
7
+ let(:current_node_name) { "q1" }
8
+ let(:start_state) { Smartdown::Engine::State.new(current_node: current_node_name) }
9
+ let(:input) { "yes" }
10
+ subject(:transition) { described_class.new(start_state, current_node, input, predicate_evaluator: predicate_evaluator) }
11
+ let(:predicate_evaluator) { instance_double("Smartdown::Engine::PredicateEvaluator") }
12
+ let(:state_including_input) {
13
+ start_state.put(current_node.name, input)
14
+ }
15
+
16
+ context "no next node rules" do
17
+ let(:current_node) {
18
+ Smartdown::Model::Node.new(current_node_name, [])
19
+ }
20
+
21
+ describe "#next_node" do
22
+ it "raises IndeterminateNextNode" do
23
+ expect { transition.next_node }.to raise_error(Smartdown::Engine::IndeterminateNextNode)
24
+ end
25
+ end
26
+ end
27
+
28
+ context "next node rules defined with a simple rule" do
29
+ let(:predicate1) { double("predicate1") }
30
+ let(:outcome_name1) { "o1" }
31
+ let(:predicate2) { double("predicate2") }
32
+ let(:outcome_name2) { "o2" }
33
+
34
+ let(:current_node) {
35
+ Smartdown::Model::Node.new(
36
+ current_node_name,
37
+ [
38
+ Smartdown::Model::NextNodeRules.new(
39
+ [
40
+ Smartdown::Model::Rule.new(
41
+ predicate1, outcome_name1
42
+ ),
43
+ Smartdown::Model::Rule.new(
44
+ predicate2, outcome_name2
45
+ )
46
+ ]
47
+ )
48
+ ]
49
+ )
50
+ }
51
+
52
+ describe "#next_node" do
53
+ it "invokes the predicate evaluator with the predicate and state including the input value" do
54
+ expect(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
55
+
56
+ transition.next_node
57
+ end
58
+
59
+ it "invokes the predicate evaluator for each rule in turn" do
60
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
61
+ expect(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
62
+
63
+ transition.next_node
64
+ end
65
+
66
+ it "returns the name of the next node" do
67
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
68
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
69
+
70
+ expect(transition.next_node).to eq(outcome_name2)
71
+ end
72
+ end
73
+
74
+ describe "#next_state" do
75
+ before(:each) do
76
+ allow(predicate_evaluator).to receive(:evaluate).and_return(true)
77
+ end
78
+
79
+ it "returns a state including a record of responses, path, and new current_node" do
80
+ expected_state = start_state
81
+ .put(:responses, [input])
82
+ .put(:path, [current_node_name])
83
+ .put(:current_node, outcome_name1)
84
+ .put(current_node.name, input)
85
+
86
+ expect(transition.next_state).to eq(expected_state)
87
+ end
88
+ end
89
+ end
90
+
91
+ context "next node rules defined with nested rule" do
92
+ let(:predicate1) { double("predicate1") }
93
+ let(:predicate2) { double("predicate2") }
94
+ let(:predicate3) { double("predicate3") }
95
+ let(:outcome_name1) { "o1" }
96
+ let(:outcome_name2) { "o2" }
97
+
98
+ let(:current_node) {
99
+ Smartdown::Model::Node.new(
100
+ current_node_name,
101
+ [
102
+ Smartdown::Model::NextNodeRules.new(
103
+ [
104
+ Smartdown::Model::NestedRule.new(
105
+ predicate1, [
106
+ Smartdown::Model::Rule.new(
107
+ predicate2, outcome_name1
108
+ ),
109
+ Smartdown::Model::Rule.new(
110
+ predicate3, outcome_name2
111
+ )
112
+ ]
113
+ )
114
+ ]
115
+ )
116
+ ]
117
+ )
118
+ }
119
+
120
+ describe "#next_node" do
121
+ context "p1 false" do
122
+ it "invokes the predicate evaluator with each predicate in turn" do
123
+ expect(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
124
+
125
+ expect { transition.next_node }.to raise_error(Smartdown::Engine::IndeterminateNextNode)
126
+ end
127
+ end
128
+
129
+ context "p1 and p2 true" do
130
+ it "invokes the predicate evaluator with each predicate in turn" do
131
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
132
+ expect(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
133
+
134
+ expect(transition.next_node).to eq(outcome_name1)
135
+ end
136
+ end
137
+
138
+ context "p1 true, p2 false, p3 true" do
139
+ it "invokes the predicate evaluator with each predicate in turn" do
140
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
141
+ allow(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(false)
142
+ expect(predicate_evaluator).to receive(:evaluate).with(predicate3, state_including_input).and_return(true)
143
+
144
+ expect(transition.next_node).to eq(outcome_name2)
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,79 @@
1
+ require 'smartdown/engine'
2
+
3
+ describe Smartdown::Engine do
4
+ let(:flow) {
5
+ build_flow("check-uk-visa") do
6
+ node("check-uk-visa") do
7
+ heading("Check uk visa")
8
+ paragraph("This is the paragraph")
9
+ start_button("what_passport_do_you_have?")
10
+ next_node_rules do
11
+ rule do
12
+ named_predicate("otherwise")
13
+ outcome("what_passport_do_you_have?")
14
+ end
15
+ end
16
+ end
17
+
18
+ node("what_passport_do_you_have?") do
19
+ heading("What passport do you have?")
20
+ multiple_choice(
21
+ greek: "Greek",
22
+ british: "British",
23
+ usa: "USA"
24
+ )
25
+ next_node_rules do
26
+ rule do
27
+ named_predicate("eea_passport?")
28
+ outcome("outcome_no_visa_needed")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ }
34
+
35
+ subject(:engine) { Smartdown::Engine.new(flow) }
36
+ let(:start_state) {
37
+ engine.default_start_state
38
+ .put(:eea_passport?, ->(state) {
39
+ %w{greek british}.include?(state.get(:what_passport_do_you_have?))
40
+ })
41
+ .put(:otherwise, true)
42
+ }
43
+
44
+ describe "#process" do
45
+ subject { engine.process(responses, start_state) }
46
+
47
+ context "start button response only" do
48
+ let(:responses) { %w{yes} }
49
+
50
+ it "is on what_passport_do_you_have?" do
51
+ expect(subject.get(:current_node)).to eq("what_passport_do_you_have?")
52
+ end
53
+
54
+ it "has recorded input" do
55
+ expect(subject.get("check-uk-visa")).to eq("yes")
56
+ end
57
+ end
58
+
59
+ context "greek passport" do
60
+ let(:responses) { %w{yes greek} }
61
+
62
+ it "is on what_passport_do_you_have?" do
63
+ expect(subject.get(:current_node)).to eq("outcome_no_visa_needed")
64
+ end
65
+
66
+ it "has recorded input" do
67
+ expect(subject.get("what_passport_do_you_have?")).to eq("greek")
68
+ end
69
+ end
70
+
71
+ context "USA passport" do
72
+ let(:responses) { %w{yes usa} }
73
+
74
+ it "raises IndeterminateNextNode error" do
75
+ expect { subject }.to raise_error(Smartdown::Engine::IndeterminateNextNode)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,14 @@
1
+ satisfies_need: 1234
2
+ meta_description: Blah blah
3
+
4
+ # My coversheet
5
+
6
+ This is the body markdown.
7
+
8
+ It has many paragraphs
9
+ of text.
10
+
11
+ * it
12
+ * can
13
+ * have
14
+ * lists
@@ -0,0 +1,3 @@
1
+ # My coversheet
2
+
3
+ Blah
@@ -0,0 +1,9 @@
1
+ # Question one
2
+
3
+ Body text line 1.
4
+
5
+ Body text
6
+ para 2.
7
+
8
+ * yes: Yes
9
+ * no: No
@@ -0,0 +1,3 @@
1
+ # Outcome one
2
+
3
+ body
@@ -0,0 +1,9 @@
1
+ # Question one
2
+
3
+ Body text line 1.
4
+
5
+ Body text
6
+ para 2.
7
+
8
+ * yes: Yes
9
+ * no: No
@@ -0,0 +1 @@
1
+ cover sheet
@@ -0,0 +1 @@
1
+ outcome one
@@ -0,0 +1 @@
1
+ question one
@@ -0,0 +1 @@
1
+ scenario one
@@ -0,0 +1,17 @@
1
+ name: border_control?
2
+
3
+ # Will you pass through UK Border Control?
4
+
5
+ You might pass through UK Border Control even if you don't leave the airport -
6
+ eg your bags aren't checked through and you need to collect them before transferring
7
+ to your outbound flight.
8
+
9
+ * yes: Yes
10
+ * no: No
11
+
12
+ -----------
13
+
14
+ # Next node
15
+
16
+ * border_control is 'yes' => transit
17
+ * border_control is 'no' => outcome_visa
@@ -0,0 +1,42 @@
1
+ require 'smartdown/model/flow'
2
+ require 'smartdown/model/node'
3
+
4
+ describe Smartdown::Model::Flow do
5
+ let(:flow_name) { "my_name" }
6
+ let(:nodes) { [] }
7
+ subject(:flow) { Smartdown::Model::Flow.new(flow_name, nodes) }
8
+
9
+ it "has a name" do
10
+ expect(subject.name).to eq(flow_name)
11
+ end
12
+
13
+ context "no nodes" do
14
+ it "has no nodes" do
15
+ expect(flow.nodes).to eq([])
16
+ end
17
+ end
18
+
19
+ context "one node" do
20
+ let(:node_name) { "chocolate?" }
21
+ let(:node) {
22
+ instance_double("Smartdown::Model::Node", name: node_name)
23
+ }
24
+ let(:nodes) { [node] }
25
+
26
+ describe "#nodes" do
27
+ it "returns a list with the node" do
28
+ expect(flow.nodes).to eq([node])
29
+ end
30
+ end
31
+
32
+ describe "#node" do
33
+ it "fetches the named node" do
34
+ expect(subject.node(node_name)).to eq(node)
35
+ end
36
+
37
+ it "raises if node not found" do
38
+ expect { subject.node("undefined node") }.to raise_error
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ require 'smartdown/model/node'
2
+
3
+ describe Smartdown::Model::Node do
4
+ let(:name) { "my node" }
5
+ let(:elements) { [] }
6
+
7
+ describe "#new" do
8
+ subject(:node) { described_class.new(name, elements) }
9
+
10
+ it "accepts name and list of body blocks" do
11
+ expect(node.name).to eq(name)
12
+ expect(node.elements).to eq(elements)
13
+ end
14
+
15
+ context "no front matter" do
16
+ let(:empty_front_matter) { Smartdown::Model::FrontMatter.new({}) }
17
+
18
+ it "defaults to empty" do
19
+ expect(node.front_matter).to eq(empty_front_matter)
20
+ end
21
+ end
22
+
23
+ context "front matter" do
24
+ let(:front_matter) { Smartdown::Model::FrontMatter.new({a: "1"}) }
25
+ subject(:node) { described_class.new(name, elements, front_matter) }
26
+
27
+ it "uses it" do
28
+ expect(node.front_matter).to eq(front_matter)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ require 'smartdown/parser/element/front_matter'
2
+
3
+ describe Smartdown::Parser::Base do
4
+
5
+ subject(:parser) { described_class.new }
6
+
7
+ describe "#ws" do
8
+ subject { parser.ws }
9
+ it { should parse("\n") }
10
+ it { should parse(" ") }
11
+ it { should parse(" ") }
12
+ end
13
+
14
+ describe "#eof" do
15
+ subject { parser.eof }
16
+
17
+ it { should parse("") }
18
+ it { should_not parse(" ") }
19
+ end
20
+
21
+ describe "#newline" do
22
+ subject { parser.newline }
23
+
24
+ # Only one newline
25
+ it { should parse("\r") }
26
+ it { should parse("\r\n") }
27
+ it { should parse("\n\r") }
28
+ it { should parse("\n") }
29
+
30
+ # Not multiple
31
+ it { should_not parse("\n\n") }
32
+ end
33
+
34
+ describe "#whitespace_terminated_string" do
35
+ subject { parser.whitespace_terminated_string }
36
+
37
+ it { should parse("a b c") }
38
+ it { should_not parse(" a") }
39
+ it { should_not parse("a ") }
40
+ end
41
+
42
+ describe "identifier" do
43
+ subject { parser.identifier }
44
+
45
+ it { should parse("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-123") }
46
+ it { should_not parse("abc_ABC-123?") }
47
+ end
48
+ end
49
+
@@ -0,0 +1,56 @@
1
+ require 'smartdown/parser/directory_input'
2
+
3
+ shared_examples "flow input interface" do
4
+ it { should respond_to(:coversheet) }
5
+ it { should respond_to(:questions) }
6
+ it { should respond_to(:outcomes) }
7
+ it { should respond_to(:scenarios) }
8
+ end
9
+
10
+ describe Smartdown::Parser::DirectoryInput do
11
+ it_should_behave_like "flow input interface"
12
+
13
+ let(:coversheet_file) {
14
+ Pathname.new("../../fixtures/directory_input/cover-sheet.txt").expand_path(__FILE__)
15
+ }
16
+
17
+ subject(:input) { described_class.new(coversheet_file) }
18
+
19
+ describe "#coversheet" do
20
+ subject { input.coversheet }
21
+
22
+ it { should be_a(Smartdown::Parser::InputFile) }
23
+
24
+ it "has name" do
25
+ expect(input.coversheet.name).to eq("cover-sheet")
26
+ end
27
+
28
+ it "reads the file contents" do
29
+ expect(input.coversheet.read).to eq("cover sheet\n")
30
+ end
31
+ end
32
+
33
+ describe "#questions" do
34
+ it "returns an InputFile for every file in the questions folder" do
35
+ expect(input.questions).to match([instance_of(Smartdown::Parser::InputFile)])
36
+ expect(input.questions.first.name).to eq("q1")
37
+ expect(input.questions.first.read).to eq("question one\n")
38
+ end
39
+ end
40
+
41
+ describe "#outcomes" do
42
+ it "returns an InputFile for every file in the outcomes folder" do
43
+ expect(input.outcomes).to match([instance_of(Smartdown::Parser::InputFile)])
44
+ expect(input.outcomes.first.name).to eq("o1")
45
+ expect(input.outcomes.first.read).to eq("outcome one\n")
46
+ end
47
+ end
48
+
49
+ describe "#scenarios" do
50
+ it "returns an InputFile for every file in the scenarios folder" do
51
+ expect(input.scenarios).to match([instance_of(Smartdown::Parser::InputFile)])
52
+ expect(input.scenarios.first.name).to eq("s1")
53
+ expect(input.scenarios.first.read).to eq("scenario one\n")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ require 'smartdown/parser/element/front_matter'
2
+ require 'smartdown/parser/node_interpreter'
3
+
4
+ describe Smartdown::Parser::Element::FrontMatter do
5
+
6
+ subject(:parser) { described_class.new }
7
+
8
+ it { should parse("a: 1\n").as(front_matter: [{name: "a", value: "1"}]) }
9
+ it { should parse("a: 1\nb: 2\n").as(front_matter: [{name: "a", value: "1"}, {name: "b", value: "2"}]) }
10
+
11
+ describe "transformed" do
12
+ let(:node_name) { "my_node" }
13
+ let(:source) { "a: 1\n" }
14
+ subject(:transformed) {
15
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
16
+ }
17
+
18
+ it { should eq([Smartdown::Model::FrontMatter.new("a"=>"1")]) }
19
+ end
20
+ end
21
+
@@ -0,0 +1,24 @@
1
+ require 'smartdown/parser/element/markdown_heading'
2
+ require 'smartdown/parser/node_interpreter'
3
+
4
+ describe Smartdown::Parser::Element::MarkdownHeading do
5
+
6
+ subject(:parser) { described_class.new }
7
+ let(:node_name) { "my_node" }
8
+ let(:heading_content) { "My heading"}
9
+ let(:source) { "# #{heading_content}" }
10
+
11
+ it { should parse(source).as(h1: heading_content) }
12
+ it { should parse("# heading\n").as(h1: "heading") }
13
+ it { should parse("# heading \n").as(h1: "heading") }
14
+ it { should parse("# heading ").as(h1: "heading") }
15
+
16
+ describe "transformed" do
17
+ subject(:transformed) {
18
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
19
+ }
20
+
21
+ it { should eq(Smartdown::Model::Element::MarkdownHeading.new(heading_content)) }
22
+ end
23
+ end
24
+
@@ -0,0 +1,28 @@
1
+ require 'smartdown/parser/element/markdown_paragraph'
2
+ require 'smartdown/parser/node_interpreter'
3
+
4
+ describe Smartdown::Parser::Element::MarkdownParagraph do
5
+
6
+ subject(:parser) { described_class.new }
7
+ let(:node_name) { "my_node" }
8
+
9
+ it { should parse("My para").as(p: "My para") }
10
+ it { should parse("My para\n").as(p: "My para\n") }
11
+ it { should parse(" My para").as(p: " My para") }
12
+ it { should parse(" My para\nsecond line").as(p: " My para\nsecond line") }
13
+ it { should parse(" My para\nsecond line\n").as(p: " My para\nsecond line\n") }
14
+ it { should parse(" My para\nsecond line \n").as(p: " My para\nsecond line \n") }
15
+ it { should_not parse("Para1\n\nPara2") }
16
+
17
+ it { should parse("a b") }
18
+
19
+ describe "transformed" do
20
+ let(:content) { "My para" }
21
+ subject(:transformed) {
22
+ Smartdown::Parser::NodeInterpreter.new(node_name, content, parser: parser).interpret
23
+ }
24
+
25
+ it { should eq(Smartdown::Model::Element::MarkdownParagraph.new(content)) }
26
+ end
27
+ end
28
+