lucid 0.1.1 → 0.2.0

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