smartdown 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +37 -0
- data/lib/smartdown/engine/conditional_resolver.rb +6 -4
- data/lib/smartdown/engine/transition.rb +5 -6
- data/lib/smartdown/model/element/question/date.rb +1 -1
- data/lib/smartdown/model/element/question/multiple_choice.rb +1 -1
- data/lib/smartdown/model/element/question/salary.rb +1 -1
- data/lib/smartdown/model/element/question/text.rb +1 -1
- data/lib/smartdown/parser/base.rb +12 -0
- data/lib/smartdown/parser/element/conditional.rb +8 -1
- data/lib/smartdown/parser/element/date_question.rb +1 -0
- data/lib/smartdown/parser/element/multiple_choice_question.rb +1 -0
- data/lib/smartdown/parser/element/salary_question.rb +1 -0
- data/lib/smartdown/parser/element/text_question.rb +1 -0
- data/lib/smartdown/parser/node_transform.rb +17 -10
- data/lib/smartdown/parser/option_pairs_transform.rb +11 -0
- data/lib/smartdown/version.rb +1 -1
- data/spec/acceptance/flow_spec.rb +4 -0
- data/spec/engine/conditional_resolver_spec.rb +82 -0
- data/spec/engine_spec.rb +6 -1
- data/spec/fixtures/acceptance/animal-example-multiple/questions/question_1.txt +3 -1
- data/spec/fixtures/acceptance/animal-example-multiple/questions/question_2.txt +1 -1
- data/spec/fixtures/acceptance/animal-example-multiple/questions/question_4.txt +10 -0
- data/spec/parser/element/conditional_spec.rb +180 -1
- data/spec/parser/element/date_question_spec.rb +28 -0
- data/spec/parser/element/multiple_choice_question_spec.rb +39 -1
- data/spec/parser/element/salary_question_spec.rb +33 -4
- data/spec/parser/element/text_question_spec.rb +33 -4
- data/spec/parser/node_parser_spec.rb +4 -2
- data/spec/parser/option_pairs_transform_spec.rb +36 -0
- data/spec/support/model_builder.rb +16 -22
- data/spec/support_specs/model_builder_spec.rb +39 -2
- metadata +16 -11
data/README.md
CHANGED
@@ -128,6 +128,18 @@ Asks for an arbitrary text input.
|
|
128
128
|
[salary: salary_value]
|
129
129
|
```
|
130
130
|
|
131
|
+
## Aliases
|
132
|
+
|
133
|
+
An alias lets you referrer to any question identifier by its question intentifer or its alias.
|
134
|
+
|
135
|
+
```markdown
|
136
|
+
## Are you Clark Kent?
|
137
|
+
|
138
|
+
[choice: clark_kent, alias: superman]
|
139
|
+
* yes: Yes
|
140
|
+
* no: No
|
141
|
+
```
|
142
|
+
|
131
143
|
## Next steps
|
132
144
|
|
133
145
|
Markdown to be displayed as part of an outcome to direct the users to other information of potential interest to them.
|
@@ -228,6 +240,31 @@ Text if true
|
|
228
240
|
$ENDIF
|
229
241
|
```
|
230
242
|
|
243
|
+
Similarly, it is also possible to specify an elseif clause. These can be
|
244
|
+
chained together indefinitely. It is also possible to keep an else
|
245
|
+
clause at the end like so:
|
246
|
+
|
247
|
+
```markdown
|
248
|
+
$IF pred1?
|
249
|
+
|
250
|
+
Text if pred1 true
|
251
|
+
|
252
|
+
$ELSEIF pred2?
|
253
|
+
|
254
|
+
Text if pred1 false and pred2 true
|
255
|
+
|
256
|
+
$ELSEIF pred3?
|
257
|
+
|
258
|
+
Text if pred1 and pred2 false, and pred3 true
|
259
|
+
|
260
|
+
$ELSE
|
261
|
+
|
262
|
+
Text if pred1, pred2, pred3 are false
|
263
|
+
|
264
|
+
$ENDIF
|
265
|
+
```
|
266
|
+
|
267
|
+
|
231
268
|
## Interpolation
|
232
269
|
|
233
270
|
It's possible to interpolate values from calculations, responses to questions, plugins etc.
|
@@ -10,20 +10,22 @@ module Smartdown
|
|
10
10
|
private
|
11
11
|
def evaluate(conditional, state)
|
12
12
|
if conditional.predicate.evaluate(state)
|
13
|
-
conditional.true_case
|
13
|
+
selected_branch = conditional.true_case
|
14
14
|
else
|
15
|
-
conditional.false_case
|
15
|
+
selected_branch = conditional.false_case
|
16
16
|
end
|
17
|
+
resolve_conditionals selected_branch, state
|
17
18
|
end
|
18
19
|
|
19
20
|
def resolve_conditionals(elements, state)
|
21
|
+
return unless elements
|
20
22
|
elements.map do |element|
|
21
|
-
if element.is_a?
|
23
|
+
if element.is_a? Smartdown::Model::Element::Conditional
|
22
24
|
evaluate(element, state)
|
23
25
|
else
|
24
26
|
element
|
25
27
|
end
|
26
|
-
end.flatten
|
28
|
+
end.flatten.compact
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -33,10 +33,6 @@ module Smartdown
|
|
33
33
|
node.start_button && node.start_button.start_node
|
34
34
|
end
|
35
35
|
|
36
|
-
def input_variable_names_from_question
|
37
|
-
node.questions.map(&:name)
|
38
|
-
end
|
39
|
-
|
40
36
|
def first_matching_rule(rules)
|
41
37
|
catch(:match) do
|
42
38
|
throw_first_matching_rule_in(rules)
|
@@ -63,8 +59,11 @@ module Smartdown
|
|
63
59
|
|
64
60
|
def state_with_responses
|
65
61
|
result = state.put(node.name, answers.map(&:to_s))
|
66
|
-
|
67
|
-
result = result.put(
|
62
|
+
node.questions.each_with_index do |question, index|
|
63
|
+
result = result.put(question.name, answers[index])
|
64
|
+
if question.alias
|
65
|
+
result = result.put(question.alias, answers[index])
|
66
|
+
end
|
68
67
|
end
|
69
68
|
result
|
70
69
|
end
|
@@ -14,6 +14,8 @@ module Smartdown
|
|
14
14
|
rule(:some_space) { space_char.repeat(1) }
|
15
15
|
rule(:ws) { ws_char.repeat }
|
16
16
|
rule(:non_ws) { non_ws.repeat }
|
17
|
+
rule(:comma) { str(",") }
|
18
|
+
rule(:colon) { str(":") }
|
17
19
|
|
18
20
|
rule(:whitespace_terminated_string) {
|
19
21
|
non_ws_char >> (non_ws_char | space_char.repeat(1) >> non_ws_char).repeat
|
@@ -30,6 +32,16 @@ module Smartdown
|
|
30
32
|
rule(:bullet) {
|
31
33
|
match('[*-]')
|
32
34
|
}
|
35
|
+
|
36
|
+
rule(:option_pair) {
|
37
|
+
comma >>
|
38
|
+
optional_space >>
|
39
|
+
identifier.as(:key) >>
|
40
|
+
colon >>
|
41
|
+
optional_space >>
|
42
|
+
identifier.as(:value) >>
|
43
|
+
optional_space
|
44
|
+
}
|
33
45
|
end
|
34
46
|
end
|
35
47
|
end
|
@@ -19,13 +19,20 @@ module Smartdown
|
|
19
19
|
(markdown_blocks_inside_conditional.as(:false_case) >> newline).maybe
|
20
20
|
}
|
21
21
|
|
22
|
+
rule(:elseif_clause) {
|
23
|
+
str("$ELSEIF ") >> (Predicates.new.as(:predicate) >>
|
24
|
+
optional_space >> newline.repeat(2) >>
|
25
|
+
(markdown_blocks_inside_conditional.as(:true_case) >> newline).maybe >>
|
26
|
+
((elseif_clause | else_clause).maybe)).as(:conditional).repeat(1,1).as(:false_case)
|
27
|
+
}
|
28
|
+
|
22
29
|
rule(:conditional_clause) {
|
23
30
|
(
|
24
31
|
str("$IF ") >>
|
25
32
|
Predicates.new.as(:predicate) >>
|
26
33
|
optional_space >> newline.repeat(2) >>
|
27
34
|
(markdown_blocks_inside_conditional.as(:true_case) >> newline).maybe >>
|
28
|
-
else_clause.maybe >>
|
35
|
+
(else_clause | elseif_clause).maybe >>
|
29
36
|
str("$ENDIF") >> optional_space >> line_ending
|
30
37
|
).as(:conditional)
|
31
38
|
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'parslet/transform'
|
2
|
+
require 'smartdown/parser/option_pairs_transform'
|
2
3
|
require 'smartdown/model/node'
|
3
4
|
require 'smartdown/model/front_matter'
|
4
5
|
require 'smartdown/model/rule'
|
@@ -64,27 +65,33 @@ module Smartdown
|
|
64
65
|
[url.to_s, label.to_s]
|
65
66
|
}
|
66
67
|
|
67
|
-
|
68
|
+
|
69
|
+
rule(:multiple_choice => {identifier: simple(:identifier), :option_pairs => subtree(:option_pairs), options: subtree(:choices)}) {
|
68
70
|
Smartdown::Model::Element::Question::MultipleChoice.new(
|
69
|
-
identifier.to_s,
|
71
|
+
identifier.to_s,
|
72
|
+
Hash[choices],
|
73
|
+
Smartdown::Parser::OptionPairs.transform(option_pairs).fetch('alias', nil),
|
70
74
|
)
|
71
75
|
}
|
72
76
|
|
73
|
-
rule(:date => {identifier: simple(:identifier)}) {
|
77
|
+
rule(:date => {identifier: simple(:identifier), :option_pairs => subtree(:option_pairs)}) {
|
74
78
|
Smartdown::Model::Element::Question::Date.new(
|
75
|
-
identifier.to_s
|
79
|
+
identifier.to_s,
|
80
|
+
Smartdown::Parser::OptionPairs.transform(option_pairs).fetch('alias', nil)
|
76
81
|
)
|
77
82
|
}
|
78
83
|
|
79
|
-
rule(:salary => {identifier: simple(:identifier)}) {
|
84
|
+
rule(:salary => {identifier: simple(:identifier), :option_pairs => subtree(:option_pairs)}) {
|
80
85
|
Smartdown::Model::Element::Question::Salary.new(
|
81
|
-
identifier.to_s
|
86
|
+
identifier.to_s,
|
87
|
+
Smartdown::Parser::OptionPairs.transform(option_pairs).fetch('alias', nil)
|
82
88
|
)
|
83
89
|
}
|
84
90
|
|
85
|
-
rule(:text => {identifier: simple(:identifier)}) {
|
91
|
+
rule(:text => {identifier: simple(:identifier), :option_pairs => subtree(:option_pairs)}) {
|
86
92
|
Smartdown::Model::Element::Question::Text.new(
|
87
|
-
identifier.to_s
|
93
|
+
identifier.to_s,
|
94
|
+
Smartdown::Parser::OptionPairs.transform(option_pairs).fetch('alias', nil)
|
88
95
|
)
|
89
96
|
}
|
90
97
|
|
@@ -146,10 +153,10 @@ module Smartdown
|
|
146
153
|
Smartdown::Model::Predicate::Function.new(name.to_s, [])
|
147
154
|
}
|
148
155
|
|
149
|
-
rule(:comparison_predicate => { varname: simple(:varname),
|
156
|
+
rule(:comparison_predicate => { varname: simple(:varname),
|
150
157
|
value: simple(:value),
|
151
158
|
operator: simple(:operator)
|
152
|
-
}) {
|
159
|
+
}) {
|
153
160
|
case operator
|
154
161
|
when "<="
|
155
162
|
Smartdown::Model::Predicate::Comparison::LessOrEqual.new(varname.to_s, value.to_s)
|
data/lib/smartdown/version.rb
CHANGED
@@ -123,6 +123,10 @@ describe Smartdown::Api::Flow do
|
|
123
123
|
specify { expect(flow.state("y",["lion", "no", "no"]).accepted_responses).to eq ["lion", "no", "no"] }
|
124
124
|
specify { expect(flow.state("y",["lion", "no", "no"]).current_answers).to eq [] }
|
125
125
|
end
|
126
|
+
|
127
|
+
context "with aliased identifier overwritten with later answer" do
|
128
|
+
specify { expect(flow.state("y",["bulldog", "lion", "no", "no"]).current_node.name).to eq "outcome_untrained_with_lions" }
|
129
|
+
end
|
126
130
|
end
|
127
131
|
end
|
128
132
|
|
@@ -57,4 +57,86 @@ describe Smartdown::Engine::ConditionalResolver do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
+
context "a node with nested conditionals" do
|
61
|
+
let(:node) {
|
62
|
+
model_builder.node("outcome_no_visa_needed") do
|
63
|
+
conditional do
|
64
|
+
named_predicate "pred1?"
|
65
|
+
true_case do
|
66
|
+
paragraph("True case")
|
67
|
+
end
|
68
|
+
false_case do
|
69
|
+
conditional do
|
70
|
+
named_predicate "pred2?"
|
71
|
+
true_case do
|
72
|
+
paragraph("False True case")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
context "first pred is true" do
|
81
|
+
let(:state) {
|
82
|
+
Smartdown::Engine::State.new(
|
83
|
+
current_node: node.name,
|
84
|
+
pred1?: true,
|
85
|
+
pred2?: "Doesn't matter"
|
86
|
+
)
|
87
|
+
}
|
88
|
+
|
89
|
+
let(:expected_node_after_presentation) {
|
90
|
+
model_builder.node("outcome_no_visa_needed") do
|
91
|
+
paragraph("True case")
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
it "should resolve the conditional and preserve the 'True case' paragraph block" do
|
96
|
+
expect(conditional_resolver.call(node, state)).to eq(expected_node_after_presentation)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
context "first pred is false" do
|
102
|
+
context "second pred is true" do
|
103
|
+
let(:state) {
|
104
|
+
Smartdown::Engine::State.new(
|
105
|
+
current_node: node.name,
|
106
|
+
pred1?: false,
|
107
|
+
pred2?: true
|
108
|
+
)
|
109
|
+
}
|
110
|
+
|
111
|
+
let(:expected_node_after_presentation) {
|
112
|
+
model_builder.node("outcome_no_visa_needed") do
|
113
|
+
paragraph("False True case")
|
114
|
+
end
|
115
|
+
}
|
116
|
+
|
117
|
+
it "should resolve the conditional and preserve the 'True case' paragraph block" do
|
118
|
+
expect(conditional_resolver.call(node, state)).to eq(expected_node_after_presentation)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "second pred is false" do
|
123
|
+
let(:state) {
|
124
|
+
Smartdown::Engine::State.new(
|
125
|
+
current_node: node.name,
|
126
|
+
pred1?: false,
|
127
|
+
pred2?: false
|
128
|
+
)
|
129
|
+
}
|
130
|
+
|
131
|
+
let(:expected_node_after_presentation) {
|
132
|
+
model_builder.node("outcome_no_visa_needed") do
|
133
|
+
end
|
134
|
+
}
|
135
|
+
|
136
|
+
it "should resolve the conditional and resolve no false case to be empty" do
|
137
|
+
expect(conditional_resolver.call(node, state)).to eq(expected_node_after_presentation)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
60
142
|
end
|
data/spec/engine_spec.rb
CHANGED
@@ -24,7 +24,8 @@ describe Smartdown::Engine do
|
|
24
24
|
greek: "Greek",
|
25
25
|
british: "British",
|
26
26
|
usa: "USA"
|
27
|
-
}
|
27
|
+
},
|
28
|
+
"passport_type?"
|
28
29
|
)
|
29
30
|
next_node_rules do
|
30
31
|
rule do
|
@@ -181,6 +182,10 @@ describe Smartdown::Engine do
|
|
181
182
|
it "has recorded input" do
|
182
183
|
expect(subject.get("what_passport_do_you_have?")).to eq("greek")
|
183
184
|
end
|
185
|
+
|
186
|
+
it "recorded input is accessiable via question alias" do
|
187
|
+
expect(subject.get("passport_type?")).to eq("greek")
|
188
|
+
end
|
184
189
|
end
|
185
190
|
|
186
191
|
context "USA passport" do
|
@@ -4,12 +4,14 @@
|
|
4
4
|
|
5
5
|
Text before the options.
|
6
6
|
|
7
|
-
[choice: question_1]
|
7
|
+
[choice: question_1, alias: type_of_feline]
|
8
8
|
* lion: Lion
|
9
9
|
* tiger: Tiger
|
10
10
|
* cat: Cat
|
11
|
+
* bulldog: Bulldog
|
11
12
|
|
12
13
|
This is an optional note after question.
|
13
14
|
|
15
|
+
* type_of_feline is 'bulldog' => question_4
|
14
16
|
* question_1 in {lion tiger} => question_2
|
15
17
|
* otherwise => outcome_safe_pet
|
@@ -102,7 +102,6 @@ SOURCE
|
|
102
102
|
}
|
103
103
|
end
|
104
104
|
end
|
105
|
-
end
|
106
105
|
|
107
106
|
context "simple IF-ELSE" do
|
108
107
|
let(:source) { <<-SOURCE
|
@@ -150,5 +149,185 @@ SOURCE
|
|
150
149
|
end
|
151
150
|
end
|
152
151
|
|
152
|
+
context "simple ELSE-IF" do
|
153
|
+
let(:source) { <<-SOURCE
|
154
|
+
$IF pred1?
|
155
|
+
|
156
|
+
#{true1_body}
|
157
|
+
|
158
|
+
$ELSEIF pred2?
|
159
|
+
|
160
|
+
#{true2_body}
|
161
|
+
|
162
|
+
$ENDIF
|
163
|
+
SOURCE
|
164
|
+
}
|
165
|
+
|
166
|
+
context "with one-line true case" do
|
167
|
+
let(:true1_body) { "Text if first predicate is true" }
|
168
|
+
let(:true2_body) { "Text if first predicate is false and the second is true" }
|
169
|
+
|
170
|
+
|
171
|
+
it {
|
172
|
+
should parse(source).as(
|
173
|
+
conditional: {
|
174
|
+
predicate: {named_predicate: "pred1?"},
|
175
|
+
true_case: [{p: "#{true1_body}\n"}],
|
176
|
+
false_case: [{conditional: {
|
177
|
+
predicate: {named_predicate: "pred2?"},
|
178
|
+
true_case: [{p: "#{true2_body}\n"}],
|
179
|
+
}}]
|
180
|
+
}
|
181
|
+
)
|
182
|
+
}
|
183
|
+
|
184
|
+
describe "transformed" do
|
185
|
+
subject(:transformed) {
|
186
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
187
|
+
}
|
188
|
+
|
189
|
+
it {
|
190
|
+
should eq(
|
191
|
+
Smartdown::Model::Element::Conditional.new(
|
192
|
+
Smartdown::Model::Predicate::Named.new("pred1?"),
|
193
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true1_body + "\n")],
|
194
|
+
[Smartdown::Model::Element::Conditional.new(
|
195
|
+
Smartdown::Model::Predicate::Named.new("pred2?"),
|
196
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true2_body + "\n")]
|
197
|
+
)]
|
198
|
+
)
|
199
|
+
)
|
200
|
+
}
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "double ELSE-IF" do
|
207
|
+
let(:source) { <<-SOURCE
|
208
|
+
$IF pred1?
|
209
|
+
|
210
|
+
#{true1_body}
|
211
|
+
|
212
|
+
$ELSEIF pred2?
|
213
|
+
|
214
|
+
#{true2_body}
|
215
|
+
|
216
|
+
$ELSEIF pred3?
|
217
|
+
|
218
|
+
#{true3_body}
|
219
|
+
|
220
|
+
$ENDIF
|
221
|
+
SOURCE
|
222
|
+
}
|
223
|
+
|
224
|
+
context "with one-line true case" do
|
225
|
+
let(:true1_body) { "Text if first predicate is true" }
|
226
|
+
let(:true2_body) { "Text if first predicate is false and the second is true" }
|
227
|
+
let(:true3_body) { "Text if first and second predicates are false and the third is true" }
|
228
|
+
|
229
|
+
it {
|
230
|
+
should parse(source).as(
|
231
|
+
conditional: {
|
232
|
+
predicate: {named_predicate: "pred1?"},
|
233
|
+
true_case: [{p: "#{true1_body}\n"}],
|
234
|
+
false_case: [{conditional: {
|
235
|
+
predicate: {named_predicate: "pred2?"},
|
236
|
+
true_case: [{p: "#{true2_body}\n"}],
|
237
|
+
false_case: [{conditional: {
|
238
|
+
predicate: {named_predicate: "pred3?"},
|
239
|
+
true_case: [{p: "#{true3_body}\n"}],
|
240
|
+
}}]
|
241
|
+
}}]
|
242
|
+
}
|
243
|
+
)
|
244
|
+
}
|
245
|
+
|
246
|
+
describe "transformed" do
|
247
|
+
subject(:transformed) {
|
248
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
249
|
+
}
|
250
|
+
|
251
|
+
it {
|
252
|
+
should eq(
|
253
|
+
Smartdown::Model::Element::Conditional.new(
|
254
|
+
Smartdown::Model::Predicate::Named.new("pred1?"),
|
255
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true1_body + "\n")],
|
256
|
+
[Smartdown::Model::Element::Conditional.new(
|
257
|
+
Smartdown::Model::Predicate::Named.new("pred2?"),
|
258
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true2_body + "\n")],
|
259
|
+
[Smartdown::Model::Element::Conditional.new(
|
260
|
+
Smartdown::Model::Predicate::Named.new("pred3?"),
|
261
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true3_body + "\n")]
|
262
|
+
)]
|
263
|
+
)]
|
264
|
+
)
|
265
|
+
)
|
266
|
+
}
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context "ELSE-IF with an ELSE" do
|
272
|
+
let(:source) { <<-SOURCE
|
273
|
+
$IF pred1?
|
274
|
+
|
275
|
+
#{true1_body}
|
276
|
+
|
277
|
+
$ELSEIF pred2?
|
278
|
+
|
279
|
+
#{true2_body}
|
280
|
+
|
281
|
+
$ELSE
|
282
|
+
|
283
|
+
#{false_body}
|
284
|
+
|
285
|
+
$ENDIF
|
286
|
+
SOURCE
|
287
|
+
}
|
288
|
+
|
289
|
+
context "with one-line true case" do
|
290
|
+
let(:true1_body) { "Text if first predicate is true" }
|
291
|
+
let(:true2_body) { "Text if first predicate is false and the second is true" }
|
292
|
+
let(:false_body) { "Text both predicates are false" }
|
293
|
+
|
294
|
+
|
295
|
+
it {
|
296
|
+
should parse(source).as(
|
297
|
+
conditional: {
|
298
|
+
predicate: {named_predicate: "pred1?"},
|
299
|
+
true_case: [{p: "#{true1_body}\n"}],
|
300
|
+
false_case: [{conditional: {
|
301
|
+
predicate: {named_predicate: "pred2?"},
|
302
|
+
true_case: [{p: "#{true2_body}\n"}],
|
303
|
+
false_case: [{p: "#{false_body}\n"}]
|
304
|
+
}}]
|
305
|
+
}
|
306
|
+
)
|
307
|
+
}
|
308
|
+
|
309
|
+
|
310
|
+
describe "transformed" do
|
311
|
+
subject(:transformed) {
|
312
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
313
|
+
}
|
314
|
+
|
315
|
+
it {
|
316
|
+
should eq(
|
317
|
+
Smartdown::Model::Element::Conditional.new(
|
318
|
+
Smartdown::Model::Predicate::Named.new("pred1?"),
|
319
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true1_body + "\n")],
|
320
|
+
[Smartdown::Model::Element::Conditional.new(
|
321
|
+
Smartdown::Model::Predicate::Named.new("pred2?"),
|
322
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(true2_body + "\n")],
|
323
|
+
[Smartdown::Model::Element::MarkdownParagraph.new(false_body + "\n")]
|
324
|
+
)]
|
325
|
+
)
|
326
|
+
)
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
end
|
153
332
|
end
|
154
333
|
|
@@ -12,6 +12,7 @@ describe Smartdown::Parser::Element::DateQuestion do
|
|
12
12
|
should parse(source).as(
|
13
13
|
date: {
|
14
14
|
identifier: "date_of_birth",
|
15
|
+
option_pairs: [],
|
15
16
|
}
|
16
17
|
)
|
17
18
|
end
|
@@ -25,4 +26,31 @@ describe Smartdown::Parser::Element::DateQuestion do
|
|
25
26
|
it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth")) }
|
26
27
|
end
|
27
28
|
end
|
29
|
+
|
30
|
+
context "with question tag and an alias" do
|
31
|
+
let(:source) { "[date: date_of_birth, alias: date_for_adoption_or_birth]" }
|
32
|
+
|
33
|
+
it "parses" do
|
34
|
+
should parse(source).as(
|
35
|
+
date: {
|
36
|
+
identifier: "date_of_birth",
|
37
|
+
option_pairs: [
|
38
|
+
{
|
39
|
+
key: 'alias',
|
40
|
+
value: 'date_for_adoption_or_birth',
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "transformed" do
|
48
|
+
let(:node_name) { "my_node" }
|
49
|
+
subject(:transformed) {
|
50
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
51
|
+
}
|
52
|
+
|
53
|
+
it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth", "date_for_adoption_or_birth")) }
|
54
|
+
end
|
55
|
+
end
|
28
56
|
end
|
@@ -21,7 +21,8 @@ describe Smartdown::Parser::Element::MultipleChoiceQuestion do
|
|
21
21
|
options: [
|
22
22
|
{value: "yes", label: "Yes"},
|
23
23
|
{value: "no", label: "No"}
|
24
|
-
]
|
24
|
+
],
|
25
|
+
option_pairs: [],
|
25
26
|
}
|
26
27
|
)
|
27
28
|
end
|
@@ -36,6 +37,43 @@ describe Smartdown::Parser::Element::MultipleChoiceQuestion do
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
context "with question tag and alias" do
|
41
|
+
let(:source) {
|
42
|
+
[
|
43
|
+
"[choice: yes_or_no, alias: no_or_yes]",
|
44
|
+
"* yes: Yes",
|
45
|
+
"* no: No"
|
46
|
+
].join("\n")
|
47
|
+
}
|
48
|
+
|
49
|
+
it "parses" do
|
50
|
+
should parse(source).as(
|
51
|
+
multiple_choice: {
|
52
|
+
identifier: "yes_or_no",
|
53
|
+
options: [
|
54
|
+
{value: "yes", label: "Yes"},
|
55
|
+
{value: "no", label: "No"},
|
56
|
+
],
|
57
|
+
option_pairs: [
|
58
|
+
{
|
59
|
+
key: 'alias',
|
60
|
+
value: 'no_or_yes',
|
61
|
+
}
|
62
|
+
],
|
63
|
+
}
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "transformed" do
|
68
|
+
let(:node_name) { "my_node" }
|
69
|
+
subject(:transformed) {
|
70
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
71
|
+
}
|
72
|
+
|
73
|
+
it { should eq(Smartdown::Model::Element::Question::MultipleChoice.new("yes_or_no", {"yes"=>"Yes", "no"=>"No"}, "no_or_yes")) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
39
77
|
context "without question tag" do
|
40
78
|
let(:source) {
|
41
79
|
[
|
@@ -10,10 +10,11 @@ describe Smartdown::Parser::Element::SalaryQuestion do
|
|
10
10
|
|
11
11
|
it "parses" do
|
12
12
|
should parse(source).as(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
salary: {
|
14
|
+
identifier: "mother_salary",
|
15
|
+
option_pairs:[],
|
16
|
+
}
|
17
|
+
)
|
17
18
|
end
|
18
19
|
|
19
20
|
describe "transformed" do
|
@@ -25,4 +26,32 @@ describe Smartdown::Parser::Element::SalaryQuestion do
|
|
25
26
|
it { should eq(Smartdown::Model::Element::Question::Salary.new("mother_salary")) }
|
26
27
|
end
|
27
28
|
end
|
29
|
+
|
30
|
+
context "with question tag and alias" do
|
31
|
+
let(:source) { "[salary: mother_salary, alias: mums_salary]" }
|
32
|
+
|
33
|
+
it "parses" do
|
34
|
+
should parse(source).as(
|
35
|
+
salary: {
|
36
|
+
identifier: "mother_salary",
|
37
|
+
option_pairs: [
|
38
|
+
{
|
39
|
+
key: 'alias',
|
40
|
+
value: 'mums_salary',
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "transformed" do
|
48
|
+
let(:node_name) { "my_node" }
|
49
|
+
subject(:transformed) {
|
50
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
51
|
+
}
|
52
|
+
|
53
|
+
it { should eq(Smartdown::Model::Element::Question::Salary.new("mother_salary", "mums_salary")) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
28
57
|
end
|
@@ -10,10 +10,11 @@ describe Smartdown::Parser::Element::TextQuestion do
|
|
10
10
|
|
11
11
|
it "parses" do
|
12
12
|
should parse(source).as(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
text: {
|
14
|
+
identifier: "hometown",
|
15
|
+
option_pairs: [],
|
16
|
+
},
|
17
|
+
)
|
17
18
|
end
|
18
19
|
|
19
20
|
describe "transformed" do
|
@@ -25,4 +26,32 @@ describe Smartdown::Parser::Element::TextQuestion do
|
|
25
26
|
it { should eq(Smartdown::Model::Element::Question::Text.new("hometown")) }
|
26
27
|
end
|
27
28
|
end
|
29
|
+
|
30
|
+
context "with question tag and alias" do
|
31
|
+
let(:source) { "[text: hometown, alias: birthplace]" }
|
32
|
+
|
33
|
+
it "parses" do
|
34
|
+
should parse(source).as(
|
35
|
+
text: {
|
36
|
+
identifier: "hometown",
|
37
|
+
option_pairs:[
|
38
|
+
{
|
39
|
+
key: 'alias',
|
40
|
+
value: 'birthplace',
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "transformed" do
|
48
|
+
let(:node_name) { "my_node" }
|
49
|
+
subject(:transformed) {
|
50
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
51
|
+
}
|
52
|
+
|
53
|
+
it { should eq(Smartdown::Model::Element::Question::Text.new("hometown", "birthplace")) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
28
57
|
end
|
@@ -75,7 +75,8 @@ SOURCE
|
|
75
75
|
options: [
|
76
76
|
{value: "yes", label: "Yes"},
|
77
77
|
{value: "no", label: "No"}
|
78
|
-
]
|
78
|
+
],
|
79
|
+
option_pairs: []}
|
79
80
|
}
|
80
81
|
]
|
81
82
|
})
|
@@ -106,7 +107,8 @@ SOURCE
|
|
106
107
|
options: [
|
107
108
|
{value: "yes", label: "Yes"},
|
108
109
|
{value: "no", label: "No"}
|
109
|
-
]
|
110
|
+
],
|
111
|
+
option_pairs: []}
|
110
112
|
},
|
111
113
|
{h1: "Next node rules"},
|
112
114
|
{next_node_rules: [{rule: {predicate: {named_predicate: "pred1?"}, outcome: "outcome"}}]}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'smartdown/parser/option_pairs_transform'
|
2
|
+
|
3
|
+
describe Smartdown::Parser::OptionPairs do
|
4
|
+
describe '.transform' do
|
5
|
+
subject(:transform) { Smartdown::Parser::OptionPairs.transform(input) }
|
6
|
+
|
7
|
+
context 'blank array' do
|
8
|
+
let(:input) { [] }
|
9
|
+
it 'returns empty hash' do
|
10
|
+
expect(transform).to eql({})
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'single array of option pairs hash passed' do
|
15
|
+
let(:input) { [{key: 'dog', value: 'woof'}] }
|
16
|
+
it 'returns hash containing key value pairs' do
|
17
|
+
expect(transform).to eql({'dog' => 'woof'})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'single array of option pairs hash passed' do
|
22
|
+
let(:input) { [{key: 'dog', value: 'woof'}, {key: 'cat', value: 'meow'}] }
|
23
|
+
it 'returns hash containing key value pairs' do
|
24
|
+
expect(transform).to eql({'dog' => 'woof', 'cat' => 'meow'})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'single array of option pairs hash passed value not string' do
|
29
|
+
let(:input) { [{key: 'number', value: 1}] }
|
30
|
+
it 'returns hash containing key value pairs calling after calling .to_s' do
|
31
|
+
expect(transform).to eql({'number' => '1'})
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -53,10 +53,10 @@ class ModelBuilder
|
|
53
53
|
@elements.last
|
54
54
|
end
|
55
55
|
|
56
|
-
def multiple_choice(name, options)
|
56
|
+
def multiple_choice(name, options, question_alias=nil)
|
57
57
|
@elements ||= []
|
58
58
|
options_with_string_keys = ::Hash[options.map {|k,v| [k.to_s, v]}]
|
59
|
-
@elements << Smartdown::Model::Element::Question::MultipleChoice.new(name, options_with_string_keys)
|
59
|
+
@elements << Smartdown::Model::Element::Question::MultipleChoice.new(name, options_with_string_keys, question_alias)
|
60
60
|
@elements.last
|
61
61
|
end
|
62
62
|
|
@@ -81,52 +81,46 @@ class ModelBuilder
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def rule(predicate = nil, outcome = nil, &block)
|
84
|
-
@predicate = predicate
|
85
|
-
@outcome = outcome
|
84
|
+
@predicate = [predicate].compact
|
85
|
+
@outcome = [outcome].compact
|
86
86
|
@rules ||= []
|
87
87
|
instance_eval(&block) if block_given?
|
88
|
-
@rules << Smartdown::Model::Rule.new(@predicate, @outcome)
|
88
|
+
@rules << Smartdown::Model::Rule.new(@predicate.pop, @outcome.pop)
|
89
89
|
@rules.last
|
90
90
|
end
|
91
91
|
|
92
92
|
def conditional(&block)
|
93
|
-
@predicate
|
94
|
-
@true_case
|
95
|
-
@false_case
|
93
|
+
@predicate ||= []
|
94
|
+
@true_case ||= []
|
95
|
+
@false_case ||= []
|
96
96
|
@elements ||= []
|
97
97
|
instance_eval(&block) if block_given?
|
98
|
-
@elements << Smartdown::Model::Element::Conditional.new(@predicate, @true_case, @false_case)
|
98
|
+
@elements << Smartdown::Model::Element::Conditional.new(@predicate.pop, [@true_case.pop], [@false_case.pop])
|
99
99
|
@elements.last
|
100
100
|
end
|
101
101
|
|
102
102
|
def true_case(&block)
|
103
|
-
@outer_elements = @elements
|
104
|
-
@elements = []
|
105
103
|
instance_eval(&block) if block_given?
|
106
|
-
@true_case
|
107
|
-
@
|
108
|
-
@true_case
|
104
|
+
@true_case << @elements.pop
|
105
|
+
@true_case.last
|
109
106
|
end
|
110
107
|
|
111
108
|
def false_case(&block)
|
112
|
-
@outer_elements = @elements
|
113
|
-
@elements = []
|
114
109
|
instance_eval(&block) if block_given?
|
115
|
-
@false_case
|
116
|
-
@
|
117
|
-
@false_case
|
110
|
+
@false_case << @elements.pop
|
111
|
+
@false_case.last
|
118
112
|
end
|
119
113
|
|
120
114
|
def named_predicate(name)
|
121
|
-
@predicate
|
115
|
+
@predicate << Smartdown::Model::Predicate::Named.new(name)
|
122
116
|
end
|
123
117
|
|
124
118
|
def set_membership_predicate(varname, values)
|
125
|
-
@predicate
|
119
|
+
@predicate << Smartdown::Model::Predicate::SetMembership.new(varname, values)
|
126
120
|
end
|
127
121
|
|
128
122
|
def outcome(name)
|
129
|
-
@outcome
|
123
|
+
@outcome << name
|
130
124
|
end
|
131
125
|
|
132
126
|
module DSL
|
@@ -15,7 +15,7 @@ describe ModelBuilder do
|
|
15
15
|
let(:node2) {
|
16
16
|
Smartdown::Model::Node.new("what_passport_do_you_have?", [
|
17
17
|
Smartdown::Model::Element::MarkdownHeading.new("What passport do you have?"),
|
18
|
-
Smartdown::Model::Element::Question::MultipleChoice.new("what_passport_do_you_have?", {"greek" => "Greek", "british" => "British"}),
|
18
|
+
Smartdown::Model::Element::Question::MultipleChoice.new("what_passport_do_you_have?", {"greek" => "Greek", "british" => "British"}, "passport_type?"),
|
19
19
|
Smartdown::Model::NextNodeRules.new([
|
20
20
|
Smartdown::Model::Rule.new(
|
21
21
|
Smartdown::Model::Predicate::Named.new("eea_passport?"),
|
@@ -44,7 +44,8 @@ describe ModelBuilder do
|
|
44
44
|
{
|
45
45
|
greek: "Greek",
|
46
46
|
british: "British"
|
47
|
-
}
|
47
|
+
},
|
48
|
+
"passport_type?"
|
48
49
|
)
|
49
50
|
next_node_rules do
|
50
51
|
rule do
|
@@ -89,6 +90,42 @@ describe ModelBuilder do
|
|
89
90
|
it "builds a conditional" do
|
90
91
|
should eq(expected)
|
91
92
|
end
|
93
|
+
|
94
|
+
context "nested conditionals" do
|
95
|
+
let(:expected) {
|
96
|
+
Smartdown::Model::Element::Conditional.new(
|
97
|
+
Smartdown::Model::Predicate::Named.new("pred1?"),
|
98
|
+
[Smartdown::Model::Element::MarkdownParagraph.new("True case")],
|
99
|
+
[Smartdown::Model::Element::Conditional.new(
|
100
|
+
Smartdown::Model::Predicate::Named.new("pred2?"),
|
101
|
+
[Smartdown::Model::Element::MarkdownParagraph.new("False True case")],
|
102
|
+
[Smartdown::Model::Element::MarkdownParagraph.new("False False case")]
|
103
|
+
)]
|
104
|
+
)
|
105
|
+
}
|
106
|
+
|
107
|
+
subject(:model) {
|
108
|
+
builder.conditional do
|
109
|
+
named_predicate "pred1?"
|
110
|
+
true_case do
|
111
|
+
paragraph("True case")
|
112
|
+
end
|
113
|
+
false_case do
|
114
|
+
conditional do
|
115
|
+
named_predicate "pred2?"
|
116
|
+
true_case do
|
117
|
+
paragraph("False True case")
|
118
|
+
end
|
119
|
+
false_case do
|
120
|
+
paragraph("False False case")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
}
|
126
|
+
|
127
|
+
it { should eq(expected) }
|
128
|
+
end
|
92
129
|
end
|
93
130
|
|
94
131
|
describe "#rule" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smartdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2014-07-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
16
|
-
requirement: &
|
16
|
+
requirement: &5594140 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.6.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *5594140
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &5593360 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *5593360
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &5592880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *5592880
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: gem_publisher
|
49
|
-
requirement: &
|
49
|
+
requirement: &5592080 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *5592080
|
58
58
|
description:
|
59
59
|
email: david.heath@digital.cabinet-office.gov.uk
|
60
60
|
executables:
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/smartdown/parser/element/markdown_heading.rb
|
86
86
|
- lib/smartdown/parser/element/text_question.rb
|
87
87
|
- lib/smartdown/parser/flow_interpreter.rb
|
88
|
+
- lib/smartdown/parser/option_pairs_transform.rb
|
88
89
|
- lib/smartdown/api/date_question.rb
|
89
90
|
- lib/smartdown/api/coversheet.rb
|
90
91
|
- lib/smartdown/api/question.rb
|
@@ -163,6 +164,7 @@ files:
|
|
163
164
|
- spec/parser/element/front_matter_spec.rb
|
164
165
|
- spec/parser/element/salary_question_spec.rb
|
165
166
|
- spec/parser/element/text_question_spec.rb
|
167
|
+
- spec/parser/option_pairs_transform_spec.rb
|
166
168
|
- spec/parser/node_parser_spec.rb
|
167
169
|
- spec/parser/snippet_pre_parser_spec.rb
|
168
170
|
- spec/support/flow_input_interface.rb
|
@@ -204,6 +206,7 @@ files:
|
|
204
206
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_3.txt
|
205
207
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_1.txt
|
206
208
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_2.txt
|
209
|
+
- spec/fixtures/acceptance/animal-example-multiple/questions/question_4.txt
|
207
210
|
- spec/fixtures/acceptance/cover-sheet/cover-sheet.txt
|
208
211
|
- spec/fixtures/acceptance/one-question/one-question.txt
|
209
212
|
- spec/fixtures/acceptance/one-question/questions/q1.txt
|
@@ -242,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
242
245
|
version: '0'
|
243
246
|
segments:
|
244
247
|
- 0
|
245
|
-
hash: -
|
248
|
+
hash: -1665618218665725274
|
246
249
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
247
250
|
none: false
|
248
251
|
requirements:
|
@@ -251,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
251
254
|
version: '0'
|
252
255
|
segments:
|
253
256
|
- 0
|
254
|
-
hash: -
|
257
|
+
hash: -1665618218665725274
|
255
258
|
requirements: []
|
256
259
|
rubyforge_project:
|
257
260
|
rubygems_version: 1.8.11
|
@@ -278,6 +281,7 @@ test_files:
|
|
278
281
|
- spec/parser/element/front_matter_spec.rb
|
279
282
|
- spec/parser/element/salary_question_spec.rb
|
280
283
|
- spec/parser/element/text_question_spec.rb
|
284
|
+
- spec/parser/option_pairs_transform_spec.rb
|
281
285
|
- spec/parser/node_parser_spec.rb
|
282
286
|
- spec/parser/snippet_pre_parser_spec.rb
|
283
287
|
- spec/support/flow_input_interface.rb
|
@@ -319,6 +323,7 @@ test_files:
|
|
319
323
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_3.txt
|
320
324
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_1.txt
|
321
325
|
- spec/fixtures/acceptance/animal-example-multiple/questions/question_2.txt
|
326
|
+
- spec/fixtures/acceptance/animal-example-multiple/questions/question_4.txt
|
322
327
|
- spec/fixtures/acceptance/cover-sheet/cover-sheet.txt
|
323
328
|
- spec/fixtures/acceptance/one-question/one-question.txt
|
324
329
|
- spec/fixtures/acceptance/one-question/questions/q1.txt
|