lucid 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/HISTORY.md +8 -0
  4. data/LICENSE +3 -0
  5. data/README.md +11 -2
  6. data/Rakefile +10 -1
  7. data/lib/autotest/discover.rb +5 -5
  8. data/lib/autotest/lucid_mixin.rb +34 -35
  9. data/lib/lucid/ast/table.rb +5 -4
  10. data/lib/lucid/cli/configuration.rb +0 -6
  11. data/lib/lucid/cli/options.rb +3 -12
  12. data/lib/lucid/formatter/condensed.rb +46 -0
  13. data/lib/lucid/formatter/html.rb +9 -3
  14. data/lib/lucid/formatter/junit.rb +6 -8
  15. data/lib/lucid/formatter/standard.rb +1 -7
  16. data/lib/lucid/generators/project/events-symbiont.rb +1 -1
  17. data/lib/lucid/platform.rb +1 -1
  18. data/lib/lucid/runtime.rb +1 -1
  19. data/lib/lucid/sequence.rb +5 -0
  20. data/lib/lucid/sequence/sequence_errors.rb +64 -0
  21. data/lib/lucid/sequence/sequence_group.rb +35 -0
  22. data/lib/lucid/sequence/sequence_phrase.rb +166 -0
  23. data/lib/lucid/sequence/sequence_steps.rb +20 -0
  24. data/lib/lucid/sequence/sequence_support.rb +26 -0
  25. data/lib/lucid/sequence/sequence_template.rb +354 -0
  26. data/lib/lucid/spec_file.rb +3 -1
  27. data/lib/lucid/step_match.rb +1 -1
  28. data/lib/lucid/wire_support/wire_packet.rb +1 -1
  29. data/lucid.gemspec +11 -9
  30. data/spec/lucid/app_spec.rb +42 -0
  31. data/spec/lucid/configuration_spec.rb +112 -0
  32. data/spec/lucid/sequences/sequence_conditional_spec.rb +74 -0
  33. data/spec/lucid/sequences/sequence_group_spec.rb +55 -0
  34. data/spec/lucid/sequences/sequence_phrase_spec.rb +122 -0
  35. data/spec/lucid/sequences/sequence_placeholder_spec.rb +56 -0
  36. data/spec/lucid/sequences/sequence_section_spec.rb +61 -0
  37. data/spec/lucid/sequences/sequence_support_spec.rb +65 -0
  38. data/spec/lucid/sequences/sequence_template_spec.rb +298 -0
  39. data/spec/spec_helper.rb +13 -0
  40. metadata +86 -54
  41. data/.rspec +0 -1
@@ -0,0 +1,56 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_template'
3
+
4
+ module Sequence
5
+ module SequenceTemplate
6
+
7
+ describe Placeholder do
8
+ subject { Placeholder.new('testing') }
9
+
10
+ context 'establishing a placeholder' do
11
+ it 'should be created with a variable name' do
12
+ expect { Placeholder.new('testing') }.not_to raise_error
13
+ end
14
+
15
+ it 'should know the name of its variable' do
16
+ expect(subject.name).to eq('testing')
17
+ end
18
+ end
19
+
20
+ context 'generating a placeholder' do
21
+ it 'should generate an empty string when an actual value is absent' do
22
+ generated_text = subject.output(Object.new, {})
23
+ expect(generated_text).to be_empty
24
+
25
+ generated_text = subject.output(Object.new, { 'testing' => nil })
26
+ expect(generated_text).to be_empty
27
+ end
28
+
29
+ it 'should generate an empty string when the context object value is absent' do
30
+ context = Object.new
31
+ def context.testing
32
+ nil
33
+ end
34
+ generated_text = subject.output(context, {})
35
+ expect(generated_text).to be_empty
36
+ end
37
+
38
+ it 'should render the actual value bound to the placeholder' do
39
+ generated_text = subject.output(Object.new, { 'testing' => 'test' })
40
+ expect(generated_text).to eq('test')
41
+ end
42
+
43
+ it 'should render the context object actual value bound to the placeholder' do
44
+ context = Object.new
45
+ def context.testing
46
+ 'test'
47
+ end
48
+ generated_text = subject.output(context, {})
49
+ expect(generated_text).to eq('test')
50
+ end
51
+
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,61 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_template'
3
+
4
+ module Sequence
5
+ module SequenceTemplate
6
+
7
+ describe Section do
8
+ subject { Section.new('testing') }
9
+
10
+ let(:example_child_elements) do
11
+ [ StaticText.new('Test '),
12
+ Placeholder.new('content'),
13
+ EOLine.new
14
+ ]
15
+ end
16
+
17
+ context 'establishing a section' do
18
+ it 'should be created with a variable name' do
19
+ expect { Section.new('testing') }.not_to raise_error
20
+ end
21
+
22
+ it 'should know the name of its variable' do
23
+ expect(subject.name).to eq('testing')
24
+ end
25
+
26
+ it 'should have no child elements by default' do
27
+ expect(subject).to have(0).children
28
+ end
29
+ end
30
+
31
+ context 'generating a section' do
32
+ it 'should add child elements' do
33
+ example_child_elements.each do |child|
34
+ subject.add_child(child)
35
+ end
36
+
37
+ expect(subject.children).to eq(example_child_elements)
38
+ end
39
+
40
+ it 'should know the names of all child placeholders' do
41
+ example_child_elements.each { |child| subject.add_child(child) }
42
+ expect(subject.variables).to eq([ 'content' ])
43
+
44
+ parent = Section.new('added')
45
+ [ subject,
46
+ StaticText.new('Content '),
47
+ Placeholder.new('tested'),
48
+ EOLine.new
49
+ ].each { |child| parent.add_child(child) }
50
+ expect(parent.variables).to eq(%w(content tested))
51
+ end
52
+
53
+ it 'should expect that its subclasses generate the children elements' do
54
+ msg = 'Method Section.output must be implemented in subclass.'
55
+ expect { subject.send(:output, Object.new, {}) }.to raise_error(NotImplementedError, msg)
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,65 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_support'
3
+
4
+ module Sequence
5
+
6
+ class TestDomain
7
+ include Sequence::SequenceSupport
8
+
9
+ attr_reader :seq_steps
10
+
11
+ def steps(generated_steps)
12
+ @seq_steps ||= {}
13
+ @seq_steps << generated_steps
14
+ end
15
+ end
16
+
17
+ describe SequenceSupport do
18
+ let(:domain) { TestDomain.new }
19
+ let(:phrase) { 'enter credentials' }
20
+
21
+ let(:example_steps) do
22
+ example = <<-EXAMPLE
23
+ Given the login page
24
+ When the username is "<username>"
25
+ And the password is "<password>"
26
+ And login is clicked
27
+ EXAMPLE
28
+ end
29
+
30
+ context 'defining a sequence' do
31
+ it 'should add a valid new sequence phrase and associated steps' do
32
+ expect { domain.add_sequence phrase, example_steps, true }.not_to raise_error
33
+ end
34
+
35
+ it 'should not add an existing sequence phrase' do
36
+ msg = "A sequence with phrase 'enter credentials' already exists."
37
+ expect { domain.add_sequence(phrase, example_steps, true) }.to raise_error(Sequence::DuplicateSequenceError, msg)
38
+ end
39
+
40
+ it 'should not allow a sequence to have steps with arguments and no way to use those arguments' do
41
+ phrase = 'fill in the credentials'
42
+ msg = "The step parameter 'username' does not appear in the phrase."
43
+ expect { domain.add_sequence(phrase, example_steps, false) }.to raise_error(Sequence::UnreachableStepParameter, msg)
44
+ end
45
+ end
46
+
47
+ context 'invoking a sequence' do
48
+ it 'should not be able to invoke an unknown sequence phrase' do
49
+ phrase = 'fill in the credentials'
50
+ msg = "Unknown sequence step with phrase: 'fill in the credentials'."
51
+ expect { domain.invoke_sequence(phrase) }.to raise_error(Sequence::UnknownSequenceError, msg)
52
+ end
53
+ end
54
+
55
+ context 'clearing sequences' do
56
+ it 'should clear all sequences from the sequence group' do
57
+ expect { domain.clear_sequences() }.not_to raise_error
58
+ expect(SequenceGroup.instance.sequence_steps).to be_empty
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,298 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_template'
3
+
4
+ module Sequence
5
+ module SequenceTemplate
6
+
7
+ describe Engine do
8
+
9
+ let(:example_template) do
10
+ example = <<-EXAMPLE
11
+ Given the login page
12
+ When the username is "<user_name>"
13
+ And the password is "<password>"
14
+ And login is clicked
15
+ EXAMPLE
16
+ end
17
+
18
+ let(:conditional_template) do
19
+ example = <<-EXAMPLE
20
+ When the first name is "<first_name>"
21
+ And the last name is "<last_name>"
22
+ <?age>
23
+ And the age is "<age>"
24
+ </age>
25
+ <?ssn>
26
+ And the ssn is "<ssn>"
27
+ </ssn>
28
+ EXAMPLE
29
+ end
30
+
31
+ subject { Engine.new(example_template) }
32
+
33
+ context 'parsing' do
34
+ def strip_chevrons(text)
35
+ text.gsub(/^<|>$/, '')
36
+ end
37
+
38
+ it 'should parse an empty text line' do
39
+ expect(Engine.parse('')).to be_empty
40
+ end
41
+
42
+ it 'should parse a text line without a parameter' do
43
+ sample_text = 'a TDL statement'
44
+ result = Engine.parse(sample_text)
45
+
46
+ expect(result).to have(1).items
47
+ expect(result[0]).to eq([:static, sample_text])
48
+ end
49
+
50
+ it 'should parse a text line that consists of only a parameter' do
51
+ sample_text = '<some_parameter>'
52
+ result = Engine.parse(sample_text)
53
+
54
+ expect(result).to have(1).items
55
+ expect(result[0]).to eq([:dynamic, strip_chevrons(sample_text)])
56
+ end
57
+
58
+ it 'should parse a text line with a parameter at the start' do
59
+ sample_text = '<some_parameter> in a TDL statement'
60
+ result = Engine.parse(sample_text)
61
+
62
+ expect(result).to have(2).items
63
+ expect(result[0]).to eq([:dynamic, 'some_parameter'])
64
+ expect(result[1]).to eq([:static, ' in a TDL statement'])
65
+ end
66
+
67
+ it 'should parse a text line with a parameter at the end' do
68
+ sample_text = 'a TDL statement with <some_parameter>'
69
+ result = Engine.parse(sample_text)
70
+
71
+ expect(result).to have(2).items
72
+ expect(result[0]).to eq([:static, 'a TDL statement with '])
73
+ expect(result[1]).to eq([:dynamic, 'some_parameter'])
74
+ end
75
+
76
+ it 'should parse a text line with a parameter in the middle' do
77
+ sample_text = 'a TDL statement with <some_parameter> in it'
78
+ result = Engine.parse(sample_text)
79
+
80
+ expect(result).to have(3).items
81
+ expect(result[0]).to eq([:static, 'a TDL statement with '])
82
+ expect(result[1]).to eq([:dynamic, 'some_parameter'])
83
+ expect(result[2]).to eq([:static, ' in it'])
84
+ end
85
+
86
+ it 'should parse a text line with two separated parameters' do
87
+ sample_text = 'TDL with <one_parameter> and with <another_parameter> in it'
88
+ result = Engine.parse(sample_text)
89
+
90
+ expect(result).to have(5).items
91
+ expect(result[0]).to eq([:static , 'TDL with '])
92
+ expect(result[1]).to eq([:dynamic, 'one_parameter'])
93
+ expect(result[2]).to eq([:static , ' and with '])
94
+ expect(result[3]).to eq([:dynamic, 'another_parameter'])
95
+ expect(result[4]).to eq([:static, ' in it'])
96
+ end
97
+
98
+ it 'should parse a text line with two consecutive parameters' do
99
+ sample_text = 'TDL with <one_parameter> <another_parameter> in it'
100
+ result = Engine.parse(sample_text)
101
+
102
+ expect(result).to have(5).items
103
+ expect(result[0]).to eq([:static, 'TDL with '])
104
+ expect(result[1]).to eq([:dynamic, 'one_parameter'])
105
+ expect(result[2]).to eq([:static, ' '])
106
+ expect(result[3]).to eq([:dynamic, 'another_parameter'])
107
+ expect(result[4]).to eq([:static, ' in it'])
108
+ end
109
+
110
+ it 'should parse a text line with escaped chevrons' do
111
+ sample_text = 'A TDL \<parameter\> is escaped'
112
+ result = Engine.parse(sample_text)
113
+
114
+ expect(result).to have(1).items
115
+ expect(result[0]).to eq([:static, sample_text])
116
+ end
117
+
118
+ it 'should parse a text line with escaped chevrons in a parameter' do
119
+ sample_text = 'A TDL with <some_\<\\>escaped\>_parameter> in it'
120
+ result = Engine.parse(sample_text)
121
+
122
+ expect(result).to have(3).items
123
+ expect(result[0]).to eq([:static, 'A TDL with '])
124
+ expect(result[1]).to eq([:dynamic, 'some_\<\\>escaped\>_parameter'])
125
+ expect(result[2]).to eq([:static, ' in it'])
126
+ end
127
+
128
+ it 'should indicate if a parameter has a missing closing chevron' do
129
+ sample_text = 'A TDL with <some_parameter that is malformed'
130
+ error_message = "Missing closing chevron '>'."
131
+ expect { Engine.parse(sample_text) }.to raise_error(StandardError, error_message)
132
+ end
133
+
134
+ it 'should indicate if a parameter has a missing opening chevron' do
135
+ sample_text = 'A TDL with some_parameter> that is malformed'
136
+ error_message = "Missing opening chevron '<'."
137
+ expect { Engine.parse(sample_text) }.to raise_error(StandardError, error_message)
138
+ end
139
+
140
+ it 'should indicate if a text has nested opening chevrons' do
141
+ sample_text = 'A TDL with <<some_parameter> > that is nested'
142
+ error_message = "Nested opening chevron '<'."
143
+ expect { Engine.parse(sample_text) }.to raise_error(StandardError, error_message)
144
+ end
145
+ end
146
+
147
+ context 'creation of source template' do
148
+ it 'should accept an empty template text' do
149
+ expect { Engine.new '' }.not_to raise_error
150
+ end
151
+
152
+ it 'should be created with a template text' do
153
+ expect { Engine.new example_template }.not_to raise_error
154
+ end
155
+
156
+ it 'should know the source text' do
157
+ expect(subject.source).to eq(example_template)
158
+ instance = Engine.new ''
159
+ expect(instance.source).to be_empty
160
+ end
161
+
162
+ it 'should indicate when a placeholder is empty or blank' do
163
+ text = example_template.sub(/user_name/, '')
164
+ msg = %q(An empty or blank parameter occurred in 'When the username is "<>"'.)
165
+ expect { Engine.new(text) }.to raise_error(Sequence::EmptyParameterError, msg)
166
+ end
167
+
168
+ it 'should indicate when a placeholder contains an invalid character' do
169
+ text = example_template.sub(/user_name/, 'user%name')
170
+ msg = "The invalid element '%' occurs in the parameter 'user%name'."
171
+ expect { Engine.new(text)}.to raise_error(Sequence::InvalidElementError, msg)
172
+ end
173
+
174
+ it 'should accept conditional sections' do
175
+ expect { Engine.new(conditional_template) }.not_to raise_error
176
+ instance = Engine.new(conditional_template)
177
+ elements = instance.instance_variable_get(:@generated_source)
178
+ sections = elements.select { |e| e.is_a?(Section) }
179
+ names = sections.map { |e| e.to_s }
180
+ expect(names).to eq(%w(<?age> <?ssn>))
181
+ end
182
+
183
+ it 'should indicate when a section has no closing tag' do
184
+ text = conditional_template.sub(/<\/age>/, '')
185
+ msg = 'Unterminated section <?age>.'
186
+ expect { Engine.new(text) }.to raise_error(StandardError, msg)
187
+ end
188
+
189
+ it 'should indicate when a closing tag has no corresponding opening tag' do
190
+ text = conditional_template.sub(/<\/age>/, '</test>')
191
+ msg = "End of section </test> does not match current section 'age'."
192
+ expect { Engine.new(text) }.to raise_error(StandardError, msg)
193
+ end
194
+
195
+ it 'should indicate when a closing tag is found without opening tag' do
196
+ text = conditional_template.sub(/<\?ssn>/, '</test>')
197
+ msg = "End of section </test> found while no corresponding section is open."
198
+ expect { Engine.new(text) }.to raise_error(StandardError, msg)
199
+ end
200
+ end
201
+
202
+ context 'rendering a template' do
203
+ it 'should know the parameters it contains' do
204
+ expect(subject.variables).to be == ['user_name', 'password']
205
+ instance = Engine.new ''
206
+ expect(instance.variables).to be_empty
207
+ end
208
+
209
+ it 'should generate the text with the actual values for parameters' do
210
+ locals = { 'user_name' => 'jnyman' }
211
+ generated_text = subject.output(Object.new, locals)
212
+ expected = <<-RESULT
213
+ Given the login page
214
+ When the username is "jnyman"
215
+ And the password is ""
216
+ And login is clicked
217
+ RESULT
218
+
219
+ expect(generated_text).to eq(expected)
220
+ end
221
+
222
+ it 'should generate the text with the actual non-string values for parameters' do
223
+ locals = { 'user_name' => 'jnyman', 'password' => 12345 }
224
+ generated_text = subject.output(Object.new, locals)
225
+
226
+ expected = <<-RESULT
227
+ Given the login page
228
+ When the username is "jnyman"
229
+ And the password is "12345"
230
+ And login is clicked
231
+ RESULT
232
+
233
+ expect(generated_text).to eq(expected)
234
+ end
235
+
236
+ it 'should generate the text with the actual values in context' do
237
+ ContextObject = Struct.new(:user_name, :password)
238
+ context = ContextObject.new('superb', 'tester')
239
+ generated_text = subject.output(context, { 'user_name' => 'jnyman' })
240
+
241
+ expected = <<-RESULT
242
+ Given the login page
243
+ When the username is "jnyman"
244
+ And the password is "tester"
245
+ And login is clicked
246
+ RESULT
247
+
248
+ expect(generated_text).to eq(expected)
249
+ end
250
+
251
+ it 'should handle an empty source template' do
252
+ instance = Engine.new('')
253
+ expect(instance.output(nil, {})).to be_empty
254
+ end
255
+
256
+ it 'should generate conditional sections' do
257
+ instance = Engine.new(conditional_template)
258
+ locals = { 'first_name' => 'Jeff',
259
+ 'last_name' => 'Nyman' ,
260
+ 'age' => '41'
261
+ }
262
+ generated_text = instance.output(Object.new, locals)
263
+ expected = <<-RESULT
264
+ When the first name is "Jeff"
265
+ And the last name is "Nyman"
266
+ And the age is "41"
267
+ RESULT
268
+ expect(generated_text).to eq(expected)
269
+
270
+ locals['age'] = nil
271
+ locals['ssn'] = '000-00-0000'
272
+ generated_text = instance.output(Object.new, locals)
273
+
274
+ expected = <<-RESULT
275
+ When the first name is "Jeff"
276
+ And the last name is "Nyman"
277
+ And the ssn is "000-00-0000"
278
+ RESULT
279
+ expect(generated_text).to eq(expected)
280
+ end
281
+
282
+ it 'should generate multi-valued actual text from parameters' do
283
+ locals = { 'user_name' => %w(jnyman tester) }
284
+ generated_text = subject.output(Object.new, locals)
285
+ expected = <<-RESULT
286
+ Given the login page
287
+ When the username is "jnyman<br/>tester"
288
+ And the password is ""
289
+ And login is clicked
290
+ RESULT
291
+ expect(generated_text).to eq(expected)
292
+ end
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+ end