cutara 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,287 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'fileutils'
3
+ require 'cucumber/formatter/console'
4
+ require 'cucumber/formatter/io'
5
+ require 'gherkin/formatter/escaping'
6
+ require 'TarantulaUpdater'
7
+ require 'Helper'
8
+
9
+ module Cucumber
10
+ module Formatter
11
+ # This formatter prints features to plain text - exactly how they were parsed,
12
+ # just prettier. That means with proper indentation and alignment of table columns.
13
+ # it also updates Tarantula step results
14
+ #
15
+ # If the output is STDOUT (and not a file), there are bright colours to watch too.
16
+ #
17
+ module Duration
18
+ # Helper method for formatters that need to
19
+ # format a duration in seconds to the UNIX
20
+ # <tt>time</tt> format.
21
+ def format_duration_simple(seconds)
22
+ seconds
23
+ end
24
+ end
25
+ class CustomTarantulaFormatter
26
+ include FileUtils
27
+ include Console
28
+ include Io
29
+ include Gherkin::Formatter::Escaping
30
+ attr_writer :indent
31
+ attr_reader :runtime
32
+
33
+ def initialize(runtime, path_or_io, options)
34
+ @runtime, @io, @options = runtime, ensure_io(path_or_io, "pretty"), options
35
+ @exceptions = []
36
+ @indent = 0
37
+ @prefixes = options[:prefixes] || {}
38
+ @delayed_messages = []
39
+ @feature_name = nil
40
+ @scenario_index = 0
41
+ @scenario_exceptions = []
42
+ @scenario_undefined = false
43
+ @scenario_updated = false
44
+ TarantulaUpdater.config = YAML.load(File.open(SUPPORT+"/tarantula.yml"))
45
+ end
46
+
47
+ def after_features(features)
48
+ print_summary(features) unless @options[:autoformat]
49
+ @io.puts(format_duration_simple(features.duration)) if features && features.duration
50
+ resp = TarantulaUpdater.update_testcase_duration(ENV["project"], ENV["execution"], @feature_name, format_duration_simple(features.duration)) if features && features.duration
51
+ @io.puts ">>>>>>>>>>>>>>>" + resp.to_s
52
+ end
53
+
54
+ def before_feature(feature)
55
+ @scenario_index = 0
56
+ @exceptions = []
57
+ @indent = 0
58
+ if @options[:autoformat]
59
+ file = File.join(@options[:autoformat], feature.file)
60
+ dir = File.dirname(file)
61
+ mkdir_p(dir) unless File.directory?(dir)
62
+ @io = ensure_file(file, "pretty")
63
+ end
64
+ end
65
+
66
+ def comment_line(comment_line)
67
+ @io.puts(comment_line.indent(@indent))
68
+ @io.flush
69
+ end
70
+
71
+ def after_tags(tags)
72
+ if @indent == 1
73
+ @io.puts
74
+ @io.flush
75
+ end
76
+ end
77
+
78
+ def tag_name(tag_name)
79
+ tag = format_string(tag_name, :tag).indent(@indent)
80
+ @io.print(tag)
81
+ @io.flush
82
+ @indent = 1
83
+ end
84
+
85
+ def feature_name(keyword, name)
86
+ @feature_name = name
87
+ @io.puts("#{keyword}: #{name}")
88
+ @io.puts
89
+ @io.flush
90
+ end
91
+
92
+ def before_feature_element(feature_element)
93
+ @indent = 2
94
+ @scenario_indent = 2
95
+ end
96
+
97
+ def after_feature_element(feature_element)
98
+ @io.puts
99
+ @io.flush
100
+ end
101
+
102
+ def before_background(background)
103
+ @indent = 2
104
+ @scenario_indent = 2
105
+ @in_background = true
106
+ end
107
+
108
+ def after_background(background)
109
+ @in_background = nil
110
+ @io.puts
111
+ @io.flush
112
+ end
113
+
114
+ def background_name(keyword, name, file_colon_line, source_indent)
115
+ print_feature_element_name(keyword, name, file_colon_line, source_indent)
116
+ end
117
+
118
+ def before_examples_array(examples_array)
119
+ @indent = 4
120
+ @io.puts
121
+ @visiting_first_example_name = true
122
+ end
123
+
124
+ def examples_name(keyword, name)
125
+ @io.puts unless @visiting_first_example_name
126
+ @visiting_first_example_name = false
127
+ names = name.strip.empty? ? [name.strip] : name.split("\n")
128
+ @io.puts(" #{keyword}: #{names[0]}")
129
+ names[1..-1].each {|s| @io.puts " #{s}" } unless names.empty?
130
+ @io.flush
131
+ @indent = 6
132
+ @scenario_indent = 6
133
+ end
134
+
135
+ def before_outline_table(outline_table)
136
+ @table = outline_table
137
+ end
138
+
139
+ def after_outline_table(outline_table)
140
+ @table = nil
141
+ @indent = 4
142
+ end
143
+
144
+ def scenario_name(keyword, name, file_colon_line, source_indent)
145
+ @scenario_index += 1
146
+ @scenario_exceptions = []
147
+ @scenario_undefined = false
148
+ print_feature_element_name(keyword, name, file_colon_line, source_indent)
149
+ end
150
+
151
+ def before_step(step)
152
+ @current_step = step
153
+ @indent = 6
154
+ end
155
+
156
+ def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
157
+ @hide_this_step = false
158
+ if exception
159
+ if @exceptions.include?(exception)
160
+ @hide_this_step = true
161
+ return
162
+ end
163
+ @exceptions << exception
164
+ @scenario_exceptions << exception
165
+ end
166
+ if status != :failed && @in_background ^ background
167
+ @hide_this_step = true
168
+ return
169
+ end
170
+ if status == :undefined
171
+ @scenario_undefined = true
172
+ end
173
+ @status = status
174
+ end
175
+
176
+ def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
177
+ return if @hide_this_step
178
+ source_indent = nil unless @options[:source]
179
+ name_to_report = format_step(keyword, step_match, status, source_indent)
180
+ @io.puts(name_to_report.indent(@scenario_indent + 2))
181
+ print_messages
182
+ end
183
+
184
+ def doc_string(string)
185
+ return if @hide_this_step
186
+ s = %{"""\n#{string}\n"""}.indent(@indent)
187
+ s = s.split("\n").map{|l| l =~ /^\s+$/ ? '' : l}.join("\n")
188
+ @io.puts(format_string(s, @current_step.status))
189
+ @io.flush
190
+ end
191
+
192
+ def exception(exception, status)
193
+ return if @hide_this_step
194
+ print_exception(exception, status, @indent)
195
+ @io.flush
196
+ end
197
+
198
+ def before_multiline_arg(multiline_arg)
199
+ return if @options[:no_multiline] || @hide_this_step
200
+ @table = multiline_arg
201
+ end
202
+
203
+ def after_multiline_arg(multiline_arg)
204
+ @table = nil
205
+ end
206
+
207
+ def before_table_row(table_row)
208
+ return if !@table || @hide_this_step
209
+ @col_index = 0
210
+ @io.print ' |'.indent(@indent-2)
211
+ end
212
+
213
+ def after_table_row(table_row)
214
+ return if !@table || @hide_this_step
215
+ print_table_row_messages
216
+ @io.puts
217
+ if table_row.exception && !@exceptions.include?(table_row.exception)
218
+ print_exception(table_row.exception, table_row.status, @indent)
219
+ end
220
+ end
221
+
222
+ def after_table_cell(cell)
223
+ return unless @table
224
+ @col_index += 1
225
+ end
226
+
227
+ def table_cell_value(value, status)
228
+ return if !@table || @hide_this_step
229
+ status ||= @status || :passed
230
+ width = @table.col_width(@col_index)
231
+ cell_text = escape_cell(value.to_s || '')
232
+ padded = cell_text + (' ' * (width - cell_text.unpack('U*').length))
233
+ prefix = cell_prefix(status)
234
+ @io.print(' ' + format_string("#{prefix}#{padded}", status) + ::Cucumber::Term::ANSIColor.reset(" |"))
235
+ @io.flush
236
+ end
237
+
238
+ def after_steps(steps)
239
+ return if @scenario_updated
240
+ result = "PASSED"
241
+ message = ''
242
+ position = @scenario_index
243
+ if not @scenario_exceptions.empty?
244
+ result = "FAILED"
245
+ message = @scenario_exceptions.inspect
246
+ @scenario_updated = true
247
+ elsif @scenario_undefined
248
+ result = "NOT_IMPL"
249
+ message = "Undefined cucumber sentence found"
250
+ @scenario_updated = true
251
+ end
252
+ if @in_background
253
+ message += " !INSIDE BACKGROUND!"
254
+ position = 1
255
+ end
256
+ resp = TarantulaUpdater.update_testcase_step(ENV["project"], ENV["execution"], @feature_name, position, result, message)
257
+ @io.puts ">>>>>>>>>>>>>>>" + resp.to_s
258
+ end
259
+
260
+ private
261
+
262
+ def print_feature_element_name(keyword, name, file_colon_line, source_indent)
263
+ @io.puts if @scenario_indent == 6
264
+ names = name.empty? ? [name] : name.split("\n")
265
+ line = "#{keyword}: #{names[0]}".indent(@scenario_indent)
266
+ @io.print(line)
267
+ if @options[:source]
268
+ line_comment = " # #{file_colon_line}".indent(source_indent)
269
+ @io.print(format_string(line_comment, :comment))
270
+ end
271
+ @io.puts
272
+ names[1..-1].each {|s| @io.puts " #{s}"}
273
+ @io.flush
274
+ end
275
+
276
+ def cell_prefix(status)
277
+ @prefixes[status]
278
+ end
279
+
280
+ def print_summary(features)
281
+ print_stats(features, @options)
282
+ print_snippets(@options)
283
+ print_passing_wip(@options)
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Cutara
3
+ class ExecutionStepError < StandardError; end
4
+ end
@@ -0,0 +1,82 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'babosa'
3
+
4
+ ROOT = Dir.pwd + "/features"
5
+ PAGES = ROOT + "/pages"
6
+ STEPS = ROOT + "/step_definitions"
7
+ SUPPORT = ROOT + "/support"
8
+ ASSETS = File.dirname(__FILE__)+"/../assets"
9
+
10
+ class String
11
+ @supported_languages = [:english,:bulgarian,:danish,:german,:greek,:macedonian,:norwegian,:romanian,:russian,:serbian,:spanish,:swedish,:ukrainian]
12
+ @@label_language = :russian
13
+
14
+ def self.label_language=(l)
15
+ raise ArgumentError, "unsupported language, supported languages: #{@supported_languages.inspect}\nmore: https://github.com/norman/babosa.git"\
16
+ unless @supported_languages.include? l
17
+ @@label_language = l
18
+ end
19
+
20
+ def self.label_language
21
+ @@label_language
22
+ end
23
+
24
+ def to_label
25
+ if String.label_language == :english
26
+ return self.to_slug.to_ruby_method.downcase
27
+ else
28
+ return self.to_slug.transliterate(String.label_language).to_ruby_method.downcase
29
+ end
30
+ end
31
+
32
+ def to_action
33
+ 'fire_'+self.to_label
34
+ end
35
+
36
+ def to_validator
37
+ 'validate_'+self.to_label
38
+ end
39
+
40
+ def to_input
41
+ 'feed_'+self.to_label
42
+ end
43
+
44
+ def to_press_action
45
+ 'press_'+self.to_label
46
+ end
47
+
48
+ def to_select
49
+ 'select_from_'+self.to_label
50
+ end
51
+
52
+ def to_each
53
+ self.to_label+'_each'
54
+ end
55
+
56
+ def to_open_subpage
57
+ self.to_label+'_open'
58
+ end
59
+
60
+ def to_params
61
+ params = {}
62
+ self.split(",").collect(&:strip).sort.each{|p|
63
+ params[p.split("=")[0].strip.to_sym] = p.split("=")[1].strip
64
+ }
65
+ params
66
+ end
67
+ end
68
+
69
+ class Dir
70
+ def recursive_each &block
71
+ self.each do |dir|
72
+ dirpath = self.path + '/' + dir
73
+ if File.directory?(dirpath) then
74
+ if dir != '.' && dir != '..' then
75
+ Dir.new(dirpath).recursive_each &block
76
+ end
77
+ else
78
+ block.call dirpath
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,62 @@
1
+ require "active_support/core_ext/hash"
2
+ require 'httparty'
3
+ require 'Helper'
4
+
5
+ class TarantulaUpdater
6
+ include HTTParty
7
+
8
+ @config = nil
9
+ # debug_output $stdout
10
+ headers 'Content-type' => 'application/xml', 'Accept' => 'application/xml'
11
+
12
+ def self.config= hash
13
+ @config = hash
14
+ end
15
+
16
+ def self.get_tests options
17
+ options = options.merge({ "language" => @config["language"] })
18
+ params = {
19
+ :basic_auth => { :username => @config["username"], :password => @config["password"] },
20
+ :body => options.to_xml(:skip_types => true, :root => "request")
21
+ }
22
+ response = self.post(@config["server"]+'/api/get_scenarios', params)
23
+ raise response.body unless response.code == 200
24
+ response
25
+ end
26
+
27
+ def self.update_testcase_step(project, execution, testcase, step_position, result, comment)
28
+ body = {
29
+ :project => project,
30
+ :execution => execution,
31
+ :testcase => testcase,
32
+ :position => step_position,
33
+ :result => result,
34
+ :comment => comment,
35
+ }.to_xml(:skip_types => true, :root => "request")
36
+
37
+ params = {
38
+ :basic_auth => { :username => @config["username"], :password => @config["password"] },
39
+ :body => body
40
+ }
41
+ response = self.post(@config["server"]+'/api/update_testcase_step', params)
42
+ raise response.body unless response.code == 200
43
+ response
44
+ end
45
+
46
+ def self.update_testcase_duration(project, execution, testcase, duration)
47
+ body = {
48
+ :project => project,
49
+ :execution => execution,
50
+ :testcase => testcase,
51
+ :duration => duration
52
+ }.to_xml(:skip_types => true, :root => "request")
53
+
54
+ params = {
55
+ :basic_auth => { :username => @config["username"], :password => @config["password"] },
56
+ :body => body
57
+ }
58
+ response = self.post(@config["server"]+'/api/update_testcase_duration', params)
59
+ raise response.body unless response.code == 200
60
+ response
61
+ end
62
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Cutara
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,43 @@
1
+ require 'TarantulaUpdater'
2
+ require 'Helper'
3
+ require "rexml/document"
4
+ include REXML
5
+ include FileUtils::Verbose
6
+
7
+ @lang = YAML.load(File.open(SUPPORT+"/tarantula.yml"))["language"] if File.exist? SUPPORT+'/tarantula.yml'
8
+ namespace "cutara" do
9
+ desc "Builds cucumber project"
10
+ task :build, [:project, :testcase, :execution] => :download do
11
+ mkdir_p(PAGES) unless File.exists?(PAGES)
12
+ mkdir_p(STEPS) unless File.exists?(STEPS)
13
+ cp "#{ASSETS}/env.rb", SUPPORT
14
+ steps_source = ASSETS + "/step_definitions/" + @lang
15
+ Dir.glob("#{steps_source}/*") {|f| cp File.expand_path(f), STEPS }
16
+ end
17
+
18
+ desc "Downloads cucumber scenarios from tarantula"
19
+ task :download, :project, :testcase, :execution do |t, args|
20
+ raise "arguments: [project*,testcase,execution]" if args.empty?
21
+
22
+ unless File.exist?(SUPPORT+"/tarantula.yml")
23
+ mkdir_p(SUPPORT) unless File.exists?(SUPPORT) # including ROOT dir
24
+ cp "#{ASSETS}/tarantula.yml", SUPPORT
25
+ raise "Please update #{SUPPORT}/tarantula.yml and rerun the task"
26
+ end
27
+ TarantulaUpdater.config = YAML.load(File.open(SUPPORT+"/tarantula.yml"))
28
+ query = {}
29
+ args.each{ |k,v| query[k.to_s]=v }
30
+ resp = TarantulaUpdater.get_tests(query)
31
+ resp = REXML::Document.new resp.body
32
+ resp.elements.each('test/test') do |test|
33
+ title = test.elements['title'].text
34
+ body = test.elements['body'].text
35
+ file = File.new(ROOT+"/#{title.to_label}.feature", "w+")
36
+ file.puts "# language: #{@lang}"
37
+ file.puts body
38
+ file.close
39
+ end
40
+ end
41
+
42
+ end
43
+