lucid 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/.gitignore +30 -10
  2. data/.rspec +1 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +15 -0
  6. data/Gemfile +4 -2
  7. data/HISTORY.md +22 -0
  8. data/{LICENSE.txt → LICENSE} +6 -3
  9. data/README.md +22 -8
  10. data/Rakefile +2 -1
  11. data/bin/lucid +10 -10
  12. data/bin/lucid-gen +4 -0
  13. data/lib/autotest/discover.rb +11 -0
  14. data/lib/autotest/lucid.rb +6 -0
  15. data/lib/autotest/lucid_mixin.rb +135 -0
  16. data/lib/autotest/lucid_rails.rb +6 -0
  17. data/lib/autotest/lucid_rails_rspec.rb +6 -0
  18. data/lib/autotest/lucid_rails_rspec2.rb +6 -0
  19. data/lib/autotest/lucid_rspec.rb +6 -0
  20. data/lib/autotest/lucid_rspec2.rb +6 -0
  21. data/lib/lucid.rb +32 -1
  22. data/lib/lucid/ast.rb +20 -0
  23. data/lib/lucid/ast/background.rb +116 -0
  24. data/lib/lucid/ast/comment.rb +24 -0
  25. data/lib/lucid/ast/doc_string.rb +44 -0
  26. data/lib/lucid/ast/empty_background.rb +33 -0
  27. data/lib/lucid/ast/examples.rb +49 -0
  28. data/lib/lucid/ast/feature.rb +99 -0
  29. data/lib/lucid/ast/has_steps.rb +74 -0
  30. data/lib/lucid/ast/location.rb +41 -0
  31. data/lib/lucid/ast/multiline_argument.rb +31 -0
  32. data/lib/lucid/ast/names.rb +13 -0
  33. data/lib/lucid/ast/outline_table.rb +194 -0
  34. data/lib/lucid/ast/scenario.rb +103 -0
  35. data/lib/lucid/ast/scenario_outline.rb +144 -0
  36. data/lib/lucid/ast/specs.rb +38 -0
  37. data/lib/lucid/ast/step.rb +122 -0
  38. data/lib/lucid/ast/step_collection.rb +92 -0
  39. data/lib/lucid/ast/step_invocation.rb +196 -0
  40. data/lib/lucid/ast/table.rb +730 -0
  41. data/lib/lucid/ast/tags.rb +28 -0
  42. data/lib/lucid/ast/tdl_walker.rb +195 -0
  43. data/lib/lucid/cli/app.rb +78 -0
  44. data/lib/lucid/cli/configuration.rb +261 -0
  45. data/lib/lucid/cli/options.rb +463 -0
  46. data/lib/lucid/cli/profile.rb +101 -0
  47. data/lib/lucid/configuration.rb +53 -0
  48. data/lib/lucid/core_ext/disable_autorunners.rb +15 -0
  49. data/lib/lucid/core_ext/instance_exec.rb +70 -0
  50. data/lib/lucid/core_ext/proc.rb +36 -0
  51. data/lib/lucid/core_ext/string.rb +9 -0
  52. data/lib/lucid/errors.rb +40 -0
  53. data/lib/lucid/factory.rb +43 -0
  54. data/lib/lucid/formatter/ansicolor.rb +168 -0
  55. data/lib/lucid/formatter/console.rb +218 -0
  56. data/lib/lucid/formatter/debug.rb +33 -0
  57. data/lib/lucid/formatter/duration.rb +11 -0
  58. data/lib/lucid/formatter/gherkin_formatter_adapter.rb +94 -0
  59. data/lib/lucid/formatter/gpretty.rb +24 -0
  60. data/lib/lucid/formatter/html.rb +610 -0
  61. data/lib/lucid/formatter/interceptor.rb +66 -0
  62. data/lib/lucid/formatter/io.rb +31 -0
  63. data/lib/lucid/formatter/jquery-min.js +154 -0
  64. data/lib/lucid/formatter/json.rb +19 -0
  65. data/lib/lucid/formatter/json_pretty.rb +10 -0
  66. data/lib/lucid/formatter/junit.rb +177 -0
  67. data/lib/lucid/formatter/lucid.css +283 -0
  68. data/lib/lucid/formatter/lucid.sass +244 -0
  69. data/lib/lucid/formatter/ordered_xml_markup.rb +24 -0
  70. data/lib/lucid/formatter/progress.rb +95 -0
  71. data/lib/lucid/formatter/rerun.rb +91 -0
  72. data/lib/lucid/formatter/standard.rb +235 -0
  73. data/lib/lucid/formatter/stepdefs.rb +14 -0
  74. data/lib/lucid/formatter/steps.rb +49 -0
  75. data/lib/lucid/formatter/summary.rb +35 -0
  76. data/lib/lucid/formatter/unicode.rb +53 -0
  77. data/lib/lucid/formatter/usage.rb +132 -0
  78. data/lib/lucid/generator.rb +21 -0
  79. data/lib/lucid/generators/project.rb +70 -0
  80. data/lib/lucid/generators/project/Gemfile.tt +6 -0
  81. data/lib/lucid/generators/project/browser-symbiont.rb +24 -0
  82. data/lib/lucid/generators/project/driver-symbiont.rb +4 -0
  83. data/lib/lucid/generators/project/errors.rb +26 -0
  84. data/lib/lucid/generators/project/events-symbiont.rb +36 -0
  85. data/lib/lucid/generators/project/lucid-symbiont.yml +6 -0
  86. data/lib/lucid/interface.rb +8 -0
  87. data/lib/lucid/interface_methods.rb +125 -0
  88. data/lib/lucid/interface_rb/matcher.rb +108 -0
  89. data/lib/lucid/interface_rb/rb_hook.rb +18 -0
  90. data/lib/lucid/interface_rb/rb_language.rb +190 -0
  91. data/lib/lucid/interface_rb/rb_lucid.rb +119 -0
  92. data/lib/lucid/interface_rb/rb_step_definition.rb +122 -0
  93. data/lib/lucid/interface_rb/rb_transform.rb +57 -0
  94. data/lib/lucid/interface_rb/rb_world.rb +136 -0
  95. data/lib/lucid/interface_rb/regexp_argument_matcher.rb +21 -0
  96. data/lib/lucid/load_path.rb +13 -0
  97. data/lib/lucid/parser.rb +2 -126
  98. data/lib/lucid/platform.rb +27 -0
  99. data/lib/lucid/rspec/allow_doubles.rb +20 -0
  100. data/lib/lucid/rspec/disallow_options.rb +27 -0
  101. data/lib/lucid/runtime.rb +200 -0
  102. data/lib/lucid/runtime/facade.rb +60 -0
  103. data/lib/lucid/runtime/interface_io.rb +60 -0
  104. data/lib/lucid/runtime/orchestrator.rb +218 -0
  105. data/lib/lucid/runtime/results.rb +64 -0
  106. data/lib/lucid/runtime/specs_loader.rb +79 -0
  107. data/lib/lucid/spec_file.rb +112 -0
  108. data/lib/lucid/step_definition_light.rb +20 -0
  109. data/lib/lucid/step_definitions.rb +13 -0
  110. data/lib/lucid/step_match.rb +99 -0
  111. data/lib/lucid/tdl_builder.rb +282 -0
  112. data/lib/lucid/term/ansicolor.rb +118 -0
  113. data/lib/lucid/unit.rb +11 -0
  114. data/lib/lucid/wire_support/configuration.rb +38 -0
  115. data/lib/lucid/wire_support/connection.rb +61 -0
  116. data/lib/lucid/wire_support/request_handler.rb +32 -0
  117. data/lib/lucid/wire_support/wire_exception.rb +32 -0
  118. data/lib/lucid/wire_support/wire_language.rb +54 -0
  119. data/lib/lucid/wire_support/wire_packet.rb +34 -0
  120. data/lib/lucid/wire_support/wire_protocol.rb +43 -0
  121. data/lib/lucid/wire_support/wire_protocol/requests.rb +125 -0
  122. data/lib/lucid/wire_support/wire_step_definition.rb +26 -0
  123. data/lucid.gemspec +25 -14
  124. metadata +220 -12
  125. data/lib/lucid/app.rb +0 -103
  126. data/lib/lucid/options.rb +0 -168
  127. data/lib/lucid/version.rb +0 -3
  128. data/lucid.yml +0 -8
@@ -0,0 +1,41 @@
1
+ module Lucid
2
+ module AST
3
+
4
+ class Location
5
+ attr_reader :file, :line
6
+
7
+ def initialize(file, line)
8
+ @file = file || raise(ArgumentError, "file is mandatory")
9
+ @line = line || raise(ArgumentError, "line is mandatory")
10
+ end
11
+
12
+ def to_s
13
+ "#{file}:#{line}"
14
+ end
15
+
16
+ def on_line(new_line)
17
+ Location.new(file, new_line)
18
+ end
19
+ end
20
+
21
+ module HasLocation
22
+ def file_colon_line
23
+ location.to_s
24
+ end
25
+
26
+ def file
27
+ location.file
28
+ end
29
+
30
+ def line
31
+ location.line
32
+ end
33
+
34
+ def location
35
+ raise('Please set @location in the constructor') unless @location
36
+ raise("@location must be an AST::Location but is a #{@location.class}") unless @location.is_a?(Location)
37
+ @location
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ require 'gherkin/rubify'
2
+
3
+ module Lucid
4
+ module AST
5
+ module MultilineArgument
6
+
7
+ class << self
8
+ include Gherkin::Rubify
9
+
10
+ def from(argument)
11
+ return unless argument
12
+ return argument if argument.respond_to?(:to_step_definition_arg)
13
+
14
+ case(rubify(argument))
15
+ when String
16
+ # TODO: This duplicates work that Gherkin already does.
17
+ # Ideally the string should be passed directly to Gherkin for parsing.
18
+ AST::DocString.new(argument, '')
19
+ when Gherkin::Formatter::Model::DocString
20
+ AST::DocString.new(argument.value, argument.content_type)
21
+ when Array
22
+ AST::Table.new(argument.map{|row| row.cells})
23
+ else
24
+ raise ArgumentError, "Lucid does not know how to convert #{argument} into a multi-line argument."
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module Lucid
2
+ module AST
3
+ module Names
4
+ attr_reader :title, :description
5
+
6
+ def name
7
+ s = @title
8
+ s += "\n#{@description}" if @description != ""
9
+ s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,194 @@
1
+ module Lucid
2
+ module AST
3
+ class OutlineTable < Table #:nodoc:
4
+ def initialize(raw, scenario_outline)
5
+ super(raw)
6
+ @scenario_outline = scenario_outline
7
+ @cells_class = ExampleRow
8
+ init
9
+ end
10
+
11
+ def init
12
+ create_step_invocations_for_example_rows!(@scenario_outline)
13
+ end
14
+
15
+ def to_sexp
16
+ init
17
+ super
18
+ end
19
+
20
+ def accept(visitor)
21
+ return if Lucid.wants_to_quit
22
+ init
23
+ cells_rows.each_with_index do |row, n|
24
+ if(visitor.configuration.expand?)
25
+ row.accept(visitor)
26
+ else
27
+ visitor.visit_table_row(row)
28
+ end
29
+ end
30
+ nil
31
+ end
32
+
33
+ def accept_hook?(hook)
34
+ @scenario_outline.accept_hook?(hook)
35
+ end
36
+
37
+ def source_tags
38
+ @scenario_outline.source_tags
39
+ end
40
+
41
+ def source_tag_names
42
+ source_tags.map { |tag| tag.name }
43
+ end
44
+
45
+ def skip_invoke!
46
+ init
47
+ example_rows.each do |cells|
48
+ cells.skip_invoke!
49
+ end
50
+ end
51
+
52
+ def create_step_invocations_for_example_rows!(scenario_outline)
53
+ return if @dunit
54
+ @dunit = true
55
+ example_rows.each do |cells|
56
+ cells.create_step_invocations!(scenario_outline)
57
+ end
58
+ end
59
+
60
+ def example_rows
61
+ cells_rows[1..-1]
62
+ end
63
+
64
+ def visit_scenario_name(visitor, row)
65
+ @scenario_outline.visit_scenario_name(visitor, row)
66
+ end
67
+
68
+ def language
69
+ @scenario_outline.language
70
+ end
71
+
72
+ class ExampleRow < Cells #:nodoc:
73
+ class InvalidForHeaderRowError < NoMethodError
74
+ def initialize(*args)
75
+ super 'This is a header row and cannot pass or fail'
76
+ end
77
+ end
78
+
79
+ attr_reader :scenario_outline # https://rspec.lighthouseapp.com/projects/16211/tickets/342
80
+
81
+ def initialize(table, cells)
82
+ super
83
+ @scenario_exception = nil
84
+ end
85
+
86
+ def source_tag_names
87
+ source_tags.map { |tag| tag.name }
88
+ end
89
+
90
+ def source_tags
91
+ @table.source_tags
92
+ end
93
+
94
+ def create_step_invocations!(scenario_outline)
95
+ @scenario_outline = scenario_outline
96
+ @step_invocations = scenario_outline.step_invocations(self)
97
+ end
98
+
99
+ def skip_invoke!
100
+ @step_invocations.each do |step_invocation|
101
+ step_invocation.skip_invoke!
102
+ end
103
+ end
104
+
105
+ def accept(visitor)
106
+ return if Lucid.wants_to_quit
107
+ visitor.configuration.expand? ? accept_expand(visitor) : accept_plain(visitor)
108
+ end
109
+
110
+ def accept_plain(visitor)
111
+ if header?
112
+ @cells.each do |cell|
113
+ cell.status = :skipped_param
114
+ visitor.visit_table_cell(cell)
115
+ end
116
+ else
117
+ visitor.runtime.with_hooks(self) do
118
+ @step_invocations.each do |step_invocation|
119
+ step_invocation.invoke(visitor.runtime, visitor.configuration)
120
+ @exception ||= step_invocation.reported_exception
121
+ end
122
+
123
+ @cells.each do |cell|
124
+ visitor.visit_table_cell(cell)
125
+ end
126
+
127
+ visitor.visit_exception(@scenario_exception, :failed) if @scenario_exception
128
+ end
129
+ end
130
+ end
131
+
132
+ def accept_expand(visitor)
133
+ if header?
134
+ else
135
+ visitor.runtime.with_hooks(self) do
136
+ @table.visit_scenario_name(visitor, self)
137
+ @step_invocations.each do |step_invocation|
138
+ visitor.visit_step(step_invocation)
139
+ @exception ||= step_invocation.reported_exception
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ def accept_hook?(hook)
146
+ @table.accept_hook?(hook)
147
+ end
148
+
149
+ def exception
150
+ @exception || @scenario_exception
151
+ end
152
+
153
+ def fail!(exception)
154
+ @scenario_exception = exception
155
+ end
156
+
157
+ # Returns true if one or more steps failed
158
+ def failed?
159
+ raise InvalidForHeaderRowError if header?
160
+ @step_invocations.failed? || !!@scenario_exception
161
+ end
162
+
163
+ # Returns true if all steps passed
164
+ def passed?
165
+ !failed?
166
+ end
167
+
168
+ # Returns the status
169
+ def status
170
+ return :failed if @scenario_exception
171
+ @step_invocations.status
172
+ end
173
+
174
+ def backtrace_line
175
+ @scenario_outline.backtrace_line(name, line)
176
+ end
177
+
178
+ def name
179
+ "| #{@cells.collect{|c| c.value }.join(' | ')} |"
180
+ end
181
+
182
+ def language
183
+ @table.language
184
+ end
185
+
186
+ private
187
+
188
+ def header?
189
+ index == 0
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,103 @@
1
+ require 'lucid/ast/has_steps'
2
+ require 'lucid/ast/names'
3
+ require 'lucid/ast/empty_background'
4
+ require 'lucid/ast/location'
5
+ require 'lucid/unit'
6
+
7
+ module Lucid
8
+ module AST
9
+ class Scenario #:nodoc:
10
+ include HasSteps
11
+ include Names
12
+ include HasLocation
13
+
14
+ attr_reader :feature_tags
15
+ attr_accessor :feature
16
+
17
+ def initialize(language, location, background, comment, tags, feature_tags, keyword, title, description, raw_steps)
18
+ @language, @location, @background, @comment, @tags, @feature_tags, @keyword, @title, @description, @raw_steps = language, location, background, comment, tags, feature_tags, keyword, title, description, raw_steps
19
+ @exception = @executed = nil
20
+ attach_steps(@raw_steps)
21
+ end
22
+
23
+ def accept(visitor)
24
+ return if Lucid.wants_to_quit
25
+
26
+ visitor.visit_comment(@comment) unless @comment.empty?
27
+ visitor.visit_tags(@tags)
28
+ visitor.visit_scenario_name(@keyword, name, file_colon_line, source_indent(first_line_length))
29
+
30
+ skip_invoke! if @background.failed?
31
+ with_visitor(visitor) do
32
+ visitor.execute(self, skip_hooks?)
33
+ end
34
+ @executed = true
35
+ end
36
+
37
+ def to_units(background)
38
+ [Unit.new(background.step_invocations + step_invocations)]
39
+ end
40
+
41
+ # Returns true if one or more steps failed.
42
+ def failed?
43
+ steps.failed? || !!@exception
44
+ end
45
+
46
+ def fail!(exception)
47
+ @exception = exception
48
+ @current_visitor.visit_exception(@exception, :failed)
49
+ end
50
+
51
+ # Returns true if all steps passed.
52
+ def passed?
53
+ !failed?
54
+ end
55
+
56
+ # Returns the first exception (if any).
57
+ def exception
58
+ @exception || steps.exception
59
+ end
60
+
61
+ # Returns the status.
62
+ def status
63
+ return :failed if @exception
64
+ steps.status
65
+ end
66
+
67
+ def to_sexp
68
+ sexp = [:scenario, line, @keyword, name]
69
+ comment = @comment.to_sexp
70
+ sexp += [comment] if comment
71
+ tags = @tags.to_sexp
72
+ sexp += tags if tags.any?
73
+ sexp += steps.to_sexp if steps.any?
74
+ sexp
75
+ end
76
+
77
+ def with_visitor(visitor)
78
+ @current_visitor = visitor
79
+ yield
80
+ @current_visitor = nil
81
+ end
82
+
83
+ def skip_invoke!
84
+ steps.skip_invoke!
85
+ end
86
+
87
+ def steps
88
+ @steps ||= @background.step_collection(step_invocations)
89
+ end
90
+
91
+ private
92
+
93
+ def step_invocations
94
+ @raw_steps.map{|step| step.step_invocation}
95
+ end
96
+
97
+ def skip_hooks?
98
+ @background.failed? || @executed
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,144 @@
1
+ require 'lucid/ast/has_steps'
2
+ require 'lucid/ast/names'
3
+ require 'lucid/ast/empty_background'
4
+
5
+ module Lucid
6
+ module AST
7
+ class ScenarioOutline #:nodoc:
8
+ include HasSteps
9
+ include Names
10
+ include HasLocation
11
+
12
+ attr_accessor :feature
13
+ attr_reader :feature_tags
14
+
15
+ module ExamplesArray #:nodoc:
16
+ def accept(visitor)
17
+ return if Lucid.wants_to_quit
18
+ each do |examples|
19
+ visitor.visit_examples(examples)
20
+ end
21
+ end
22
+ end
23
+
24
+ # The +example_sections+ argument must be an Array where each element
25
+ # of the arry is another array representing an Examples section.
26
+ def initialize(language, location, background, comment, tags, feature_tags, keyword, title, description, raw_steps, example_sections)
27
+ @language, @location, @background, @comment, @tags, @feature_tags, @keyword, @title, @description, @raw_steps, @example_sections = language, location, background, comment, tags, feature_tags, keyword, title, description, raw_steps, example_sections
28
+ attach_steps(@raw_steps)
29
+ end
30
+
31
+ def accept(visitor)
32
+ return if Lucid.wants_to_quit
33
+ raise_missing_examples_error unless @example_sections
34
+
35
+ visitor.visit_comment(@comment) unless @comment.empty?
36
+ visitor.visit_tags(@tags)
37
+ visitor.visit_scenario_name(@keyword, name, file_colon_line, source_indent(first_line_length))
38
+ visitor.visit_steps(steps)
39
+
40
+ skip_invoke! if @background.failed?
41
+ visitor.visit_examples_array(examples_array) unless examples_array.empty?
42
+ end
43
+
44
+ def to_units(background)
45
+ raise ArgumentError.new("#{background} != #{@background}") unless background == @background # maybe we don't need this argument, but it seems like the leaf AST nodes would be better not being aware of their parents. However step_invocations uses the ivar at the moment, so we'll just do this check to make sure its OK.
46
+ result = []
47
+ each_example_row do |row|
48
+ result << Unit.new(step_invocations(row))
49
+ end
50
+ result
51
+ end
52
+
53
+ def fail!(exception)
54
+ # TODO: This is weak and is in fact just a hack.
55
+ # This simply makes sure that a scenario outline that encounters
56
+ # an exception just fails rather than causing a runtime error
57
+ # and stack trace.
58
+ end
59
+
60
+ def skip_invoke!
61
+ examples_array.each { |examples| examples.skip_invoke! }
62
+ end
63
+
64
+ def step_invocations(cells)
65
+ step_invocations = steps.step_invocations_from_cells(cells)
66
+ if @background
67
+ @background.step_collection(step_invocations)
68
+ else
69
+ StepCollection.new(step_invocations)
70
+ end
71
+ end
72
+
73
+ def each_example_row(&proc)
74
+ examples_array.each do |examples|
75
+ examples.each_example_row(&proc)
76
+ end
77
+ end
78
+
79
+ def visit_scenario_name(visitor, row)
80
+ visitor.visit_scenario_name(
81
+ language.keywords('scenario')[0],
82
+ row.name,
83
+ Location.new(file, row.line).to_s,
84
+ source_indent(first_line_length)
85
+ )
86
+ end
87
+
88
+ def failed?
89
+ examples_array.select { |examples| examples.failed? }.any?
90
+ end
91
+
92
+ def to_sexp
93
+ sexp = [:scenario_outline, @keyword, name]
94
+ comment = @comment.to_sexp
95
+ sexp += [comment] if comment
96
+ tags = @tags.to_sexp
97
+ sexp += tags if tags.any?
98
+ sexp += steps.to_sexp if steps.any?
99
+ sexp += examples_array.map{|e| e.to_sexp}
100
+ sexp
101
+ end
102
+
103
+ private
104
+
105
+ attr_reader :line
106
+
107
+ def examples_array
108
+ return @examples_array if @examples_array
109
+ @examples_array = @example_sections.map do |section|
110
+ create_examples_table(section)
111
+ end
112
+ @examples_array.extend(ExamplesArray)
113
+ @examples_array
114
+ end
115
+
116
+ def create_examples_table(example_section_and_gherkin_examples)
117
+ example_section = example_section_and_gherkin_examples[0]
118
+ gherkin_examples = example_section_and_gherkin_examples[1]
119
+
120
+ examples_location = example_section[0]
121
+ examples_comment = example_section[1]
122
+ examples_keyword = example_section[2]
123
+ examples_title = example_section[3]
124
+ examples_description = example_section[4]
125
+ examples_matrix = example_section[5]
126
+
127
+ examples_table = OutlineTable.new(examples_matrix, self)
128
+ ex = Examples.new(examples_location, examples_comment, examples_keyword, examples_title, examples_description, examples_table)
129
+ ex.gherkin_statement(gherkin_examples)
130
+ ex
131
+ end
132
+
133
+ def steps
134
+ @steps ||= StepCollection.new(@raw_steps)
135
+ end
136
+
137
+ def raise_missing_examples_error
138
+ raise MissingExamples, "Missing Example section for Scenario Outline at #{@location}."
139
+ end
140
+
141
+ MissingExamples = Class.new(StandardError)
142
+ end
143
+ end
144
+ end