cutara 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/assets/cukes.feature +170 -0
- data/assets/env.rb +13 -0
- data/assets/step_definitions/ru/action_steps.rb +25 -0
- data/assets/step_definitions/ru/data_input_steps.rb +44 -0
- data/assets/step_definitions/ru/open_steps.rb +26 -0
- data/assets/step_definitions/ru/pagination_steps.rb +25 -0
- data/assets/step_definitions/ru/press_steps.rb +27 -0
- data/assets/step_definitions/ru/table_steps.rb +66 -0
- data/assets/step_definitions/ru/validator_steps.rb +60 -0
- data/assets/tarantula.yml +4 -0
- data/cutara.gemspec +28 -0
- data/generation/Action.rb +20 -0
- data/generation/Application.rb +80 -0
- data/generation/Clickable.rb +14 -0
- data/generation/Eset.rb +18 -0
- data/generation/Input.rb +18 -0
- data/generation/Page.rb +62 -0
- data/generation/PageElement.rb +11 -0
- data/generation/Table.rb +22 -0
- data/generation/Validator.rb +12 -0
- data/generation/main/env.rb +24 -0
- data/generation/main/generate_steps.rb +205 -0
- data/lib/CucumberTarantulaFormatter.rb +287 -0
- data/lib/Exceptions.rb +4 -0
- data/lib/Helper.rb +82 -0
- data/lib/TarantulaUpdater.rb +62 -0
- data/lib/version.rb +4 -0
- data/tasks/build.rake +43 -0
- data/tasks/exec.rake +14 -0
- data/tasks/generate.rake +13 -0
- data/tasks/known.rake +24 -0
- data/tasks/local_exec.rake +8 -0
- metadata +212 -0
@@ -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
|
data/lib/Exceptions.rb
ADDED
data/lib/Helper.rb
ADDED
@@ -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
|
data/lib/version.rb
ADDED
data/tasks/build.rake
ADDED
@@ -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
|
+
|