cutara 0.0.1
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.
- 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
|
+
|