allure-ruby-commons 2.13.1 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -7
- data/lib/allure-ruby-commons.rb +172 -133
- data/lib/allure_ruby_commons/allure_lifecycle.rb +43 -46
- data/lib/allure_ruby_commons/config.rb +33 -16
- data/lib/allure_ruby_commons/file_writer.rb +36 -3
- data/lib/allure_ruby_commons/model/{jsonable.rb → 01_jsonable.rb} +14 -6
- data/lib/allure_ruby_commons/model/attachment.rb +2 -2
- data/lib/allure_ruby_commons/model/category.rb +19 -0
- data/lib/allure_ruby_commons/model/executable_item.rb +13 -6
- data/lib/allure_ruby_commons/model/label.rb +2 -2
- data/lib/allure_ruby_commons/model/link.rb +2 -2
- data/lib/allure_ruby_commons/model/parameter.rb +2 -2
- data/lib/allure_ruby_commons/model/status.rb +1 -0
- data/lib/allure_ruby_commons/model/status_details.rb +2 -0
- data/lib/allure_ruby_commons/model/test_result.rb +22 -2
- data/lib/allure_ruby_commons/model/test_result_container.rb +2 -1
- data/lib/allure_ruby_commons/result_utils.rb +37 -22
- data/lib/allure_ruby_commons/step_annotation.rb +39 -0
- data/lib/allure_ruby_commons/testplan.rb +35 -0
- metadata +45 -24
@@ -1,26 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "logger"
|
4
|
+
require "singleton"
|
4
5
|
|
5
6
|
module Allure
|
6
7
|
# Allure configuration class
|
7
8
|
class Config
|
8
|
-
|
9
|
-
|
10
|
-
# @return [String]
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
# @return [Array<String>] valid log levels
|
12
|
+
LOGLEVELS = %w[DEBUG INFO WARN ERROR FATAL UNKNOWN].freeze
|
13
|
+
|
14
|
+
attr_writer :environment, :logger
|
15
|
+
|
16
|
+
attr_accessor :results_directory,
|
17
|
+
:logging_level,
|
18
|
+
:link_tms_pattern,
|
19
|
+
:link_issue_pattern,
|
20
|
+
:clean_results_directory
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@results_directory = "reports/allure-results"
|
24
|
+
@logging_level = LOGLEVELS.index(ENV.fetch("ALLURE_LOG_LEVEL", "INFO")) || Logger::INFO
|
25
|
+
end
|
26
|
+
|
27
|
+
# Allure environment
|
28
|
+
#
|
29
|
+
# @return [String]
|
30
|
+
def environment
|
31
|
+
return(@environment) if defined?(@environment)
|
32
|
+
|
33
|
+
@environment ||= ENV["ALLURE_ENVIRONMENT"]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Logger instance
|
37
|
+
#
|
38
|
+
# @return [Logger]
|
39
|
+
def logger
|
40
|
+
@logger ||= Logger.new($stdout, level: logging_level)
|
24
41
|
end
|
25
42
|
end
|
26
43
|
end
|
@@ -9,19 +9,30 @@ module Allure
|
|
9
9
|
TEST_RESULT_CONTAINER_SUFFIX = "-container.json"
|
10
10
|
# @return [String] attachment file suffix
|
11
11
|
ATTACHMENT_FILE_SUFFIX = "-attachment"
|
12
|
+
# @return [String] environment info file
|
13
|
+
ENVIRONMENT_FILE = "environment.properties"
|
14
|
+
# @return [String] categories definition json
|
15
|
+
CATEGORIES_FILE = "categories.json"
|
16
|
+
|
17
|
+
# File writer instance
|
18
|
+
#
|
19
|
+
# @param [String] results_directory
|
20
|
+
def initialize(results_directory)
|
21
|
+
@results_directory = results_directory
|
22
|
+
end
|
12
23
|
|
13
24
|
# Write test result
|
14
25
|
# @param [Allure::TestResult] test_result
|
15
26
|
# @return [void]
|
16
27
|
def write_test_result(test_result)
|
17
|
-
write("#{test_result.uuid}#{TEST_RESULT_SUFFIX}", test_result
|
28
|
+
write("#{test_result.uuid}#{TEST_RESULT_SUFFIX}", Oj.dump(test_result))
|
18
29
|
end
|
19
30
|
|
20
31
|
# Write test result container
|
21
32
|
# @param [Allure::TestResultContainer] test_container_result
|
22
33
|
# @return [void]
|
23
34
|
def write_test_result_container(test_container_result)
|
24
|
-
write("#{test_container_result.uuid}#{TEST_RESULT_CONTAINER_SUFFIX}", test_container_result
|
35
|
+
write("#{test_container_result.uuid}#{TEST_RESULT_CONTAINER_SUFFIX}", Oj.dump(test_container_result))
|
25
36
|
end
|
26
37
|
|
27
38
|
# Write allure attachment file
|
@@ -32,10 +43,32 @@ module Allure
|
|
32
43
|
source.is_a?(File) ? copy(source.path, attachment.source) : write(attachment.source, source)
|
33
44
|
end
|
34
45
|
|
46
|
+
# Write allure report environment info
|
47
|
+
# @param [Hash<Symbol, String>] environment
|
48
|
+
# @return [void]
|
49
|
+
def write_environment(environment)
|
50
|
+
environment.reduce("") { |e, (k, v)| e + "#{k}=#{v}\n" }.tap do |env|
|
51
|
+
write(ENVIRONMENT_FILE, env)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Write categories info
|
56
|
+
# @param [File, Array<Allure::Category>] categories
|
57
|
+
# @return [void]
|
58
|
+
def write_categories(categories)
|
59
|
+
if categories.is_a?(File)
|
60
|
+
copy(categories.path, CATEGORIES_FILE)
|
61
|
+
else
|
62
|
+
write(CATEGORIES_FILE, Oj.dump(categories))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
35
66
|
private
|
36
67
|
|
68
|
+
attr_reader :results_directory
|
69
|
+
|
37
70
|
def output_dir
|
38
|
-
@output_dir ||= FileUtils.mkpath(
|
71
|
+
@output_dir ||= FileUtils.mkpath(results_directory).first
|
39
72
|
end
|
40
73
|
|
41
74
|
def write(name, source)
|
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "oj"
|
4
4
|
|
5
5
|
module Allure
|
6
6
|
# General jsonable object implementation
|
7
7
|
class JSONable
|
8
|
-
|
8
|
+
Oj.default_options = { mode: :custom, use_to_hash: true, ascii_only: true }
|
9
|
+
|
10
|
+
# Return object hash represantation
|
11
|
+
# @return [Hash]
|
12
|
+
def to_hash
|
9
13
|
instance_variables.each_with_object({}) do |var, map|
|
10
14
|
key = camelcase(var.to_s.delete_prefix("@"))
|
11
15
|
value = instance_variable_get(var)
|
@@ -13,22 +17,26 @@ module Allure
|
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
# Object comparator
|
21
|
+
# @param [JSONable] other
|
22
|
+
# @return [Booelan]
|
20
23
|
def ==(other)
|
21
24
|
self.class == other.class && state == other.state
|
22
25
|
end
|
23
26
|
|
24
27
|
protected
|
25
28
|
|
29
|
+
# Object state
|
30
|
+
# @return [Array]
|
26
31
|
def state
|
27
32
|
instance_variables.map { |var| instance_variable_get(var) }
|
28
33
|
end
|
29
34
|
|
30
35
|
private
|
31
36
|
|
37
|
+
# Covert string to camelcase
|
38
|
+
# @param [String] str
|
39
|
+
# @return [String]
|
32
40
|
def camelcase(str)
|
33
41
|
str = str.gsub(/(?:_+)([a-z])/) { Regexp.last_match(1).upcase }
|
34
42
|
str.gsub(/(\A|\s)([A-Z])/) { Regexp.last_match(1) + Regexp.last_match(2).downcase }
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "jsonable"
|
4
|
-
|
5
3
|
module Allure
|
6
4
|
# Allure model attachment object
|
7
5
|
class Attachment < JSONable
|
@@ -9,6 +7,8 @@ module Allure
|
|
9
7
|
# @param [String] type attachment type, {Allure::ContentType}
|
10
8
|
# @param [String] source attachment file name
|
11
9
|
def initialize(name:, type:, source:)
|
10
|
+
super()
|
11
|
+
|
12
12
|
@name = name
|
13
13
|
@type = type
|
14
14
|
@source = source
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Allure
|
4
|
+
# Defects category
|
5
|
+
class Category < JSONable
|
6
|
+
# @param [String] name
|
7
|
+
# @param [Array<Allure::Status>] matched_statuses
|
8
|
+
# @param [String, Regexp] message_regex
|
9
|
+
# @param [String, Regexp] trace_regex
|
10
|
+
def initialize(name:, matched_statuses: nil, message_regex: nil, trace_regex: nil)
|
11
|
+
super()
|
12
|
+
|
13
|
+
@name = name
|
14
|
+
@matched_statuses = matched_statuses
|
15
|
+
@message_regex = message_regex
|
16
|
+
@trace_regex = trace_regex
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "jsonable"
|
4
|
-
|
5
3
|
module Allure
|
6
4
|
# Allure model executable item
|
7
5
|
class ExecutableItem < JSONable
|
@@ -16,6 +14,8 @@ module Allure
|
|
16
14
|
# @option options [Array<Allure::Attachment>] :attachments ([])
|
17
15
|
# @option options [Array<Allure::Parameter>] :parameters ([])
|
18
16
|
def initialize(**options)
|
17
|
+
super()
|
18
|
+
|
19
19
|
@name = options[:name]
|
20
20
|
@description = options[:description]
|
21
21
|
@description_html = options[:description_html]
|
@@ -27,9 +27,16 @@ module Allure
|
|
27
27
|
@parameters = options[:parameters] || []
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_accessor
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
attr_accessor :name,
|
31
|
+
:status,
|
32
|
+
:status_details,
|
33
|
+
:stage,
|
34
|
+
:description,
|
35
|
+
:description_html,
|
36
|
+
:steps,
|
37
|
+
:attachments,
|
38
|
+
:parameters,
|
39
|
+
:start,
|
40
|
+
:stop
|
34
41
|
end
|
35
42
|
end
|
@@ -5,6 +5,7 @@ module Allure
|
|
5
5
|
class TestResult < ExecutableItem
|
6
6
|
# @param [String] uuid
|
7
7
|
# @param [String] history_id
|
8
|
+
# @param [String] environment
|
8
9
|
# @param [Hash] options
|
9
10
|
# @option options [String] :name
|
10
11
|
# @option options [String] :full_name
|
@@ -18,8 +19,10 @@ module Allure
|
|
18
19
|
# @option options [Array<Allure::Link>] :links ([])
|
19
20
|
# @option options [Array<Allure::Attachment>] :attachments ([])
|
20
21
|
# @option options [Array<Allure::Parameter>] :parameters ([])
|
21
|
-
def initialize(uuid: UUID.generate, history_id: UUID.generate, **options)
|
22
|
+
def initialize(uuid: UUID.generate, history_id: UUID.generate, environment: nil, **options)
|
22
23
|
super
|
24
|
+
|
25
|
+
@name = test_name(options[:name], environment)
|
23
26
|
@uuid = uuid
|
24
27
|
@history_id = history_id
|
25
28
|
@full_name = options[:full_name] || "Unnamed"
|
@@ -27,6 +30,23 @@ module Allure
|
|
27
30
|
@links = options[:links] || []
|
28
31
|
end
|
29
32
|
|
30
|
-
attr_accessor :uuid,
|
33
|
+
attr_accessor :uuid,
|
34
|
+
:history_id,
|
35
|
+
:full_name,
|
36
|
+
:labels,
|
37
|
+
:links
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Test name prefixed with allure environment
|
42
|
+
#
|
43
|
+
# @param [String] name
|
44
|
+
# @param [String] environment
|
45
|
+
# @return [String]
|
46
|
+
def test_name(name, environment)
|
47
|
+
return name unless environment
|
48
|
+
|
49
|
+
"#{environment}: #{name}"
|
50
|
+
end
|
31
51
|
end
|
32
52
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "jsonable"
|
4
3
|
module Allure
|
5
4
|
# Allure model step result container
|
6
5
|
class TestResultContainer < JSONable
|
7
6
|
def initialize(uuid: UUID.generate, name: "Unnamed")
|
7
|
+
super()
|
8
|
+
|
8
9
|
@uuid = uuid
|
9
10
|
@name = name
|
10
11
|
@children = []
|
@@ -46,14 +46,26 @@ module Allure
|
|
46
46
|
Label.new(HOST_LABEL_NAME, Socket.gethostname)
|
47
47
|
end
|
48
48
|
|
49
|
+
# Language label
|
50
|
+
# @return [Allure::Label]
|
49
51
|
def language_label
|
50
52
|
Label.new(LANGUAGE_LABEL_NAME, "ruby")
|
51
53
|
end
|
52
54
|
|
55
|
+
# Framework label
|
56
|
+
# @param [String] value
|
57
|
+
# @return [Allure::Label]
|
53
58
|
def framework_label(value)
|
54
59
|
Label.new(FRAMEWORK_LABEL_NAME, value)
|
55
60
|
end
|
56
61
|
|
62
|
+
# Epic label
|
63
|
+
# @param [String] value
|
64
|
+
# @return [Allure::Label]
|
65
|
+
def epic_label(value)
|
66
|
+
Label.new(EPIC_LABEL_NAME, value)
|
67
|
+
end
|
68
|
+
|
57
69
|
# Feature label
|
58
70
|
# @param [String] value
|
59
71
|
# @return [Allure::Label]
|
@@ -61,6 +73,13 @@ module Allure
|
|
61
73
|
Label.new(FEATURE_LABEL_NAME, value)
|
62
74
|
end
|
63
75
|
|
76
|
+
# Story label
|
77
|
+
# @param [String] value
|
78
|
+
# @return [Allure::Label]
|
79
|
+
def story_label(value)
|
80
|
+
Label.new(STORY_LABEL_NAME, value)
|
81
|
+
end
|
82
|
+
|
64
83
|
# Package label
|
65
84
|
# @param [String] value
|
66
85
|
# @return [Allure::Label]
|
@@ -89,13 +108,6 @@ module Allure
|
|
89
108
|
Label.new(SUB_SUITE_LABEL_NAME, value)
|
90
109
|
end
|
91
110
|
|
92
|
-
# Story label
|
93
|
-
# @param [String] value
|
94
|
-
# @return [Allure::Label]
|
95
|
-
def story_label(value)
|
96
|
-
Label.new(STORY_LABEL_NAME, value)
|
97
|
-
end
|
98
|
-
|
99
111
|
# Test case label
|
100
112
|
# @param [String] value
|
101
113
|
# @return [Allure::Label]
|
@@ -119,23 +131,25 @@ module Allure
|
|
119
131
|
|
120
132
|
# TMS link
|
121
133
|
# @param [String] value
|
134
|
+
# @param [String] link_pattern
|
122
135
|
# @return [Allure::Link]
|
123
|
-
def tms_link(value)
|
124
|
-
Link.new(TMS_LINK_TYPE, value,
|
136
|
+
def tms_link(value, link_pattern)
|
137
|
+
Link.new(TMS_LINK_TYPE, value, url(value, link_pattern))
|
125
138
|
end
|
126
139
|
|
127
140
|
# Issue link
|
128
141
|
# @param [String] value
|
142
|
+
# @param [String] link_pattern
|
129
143
|
# @return [Allure::Link]
|
130
|
-
def issue_link(value)
|
131
|
-
Link.new(ISSUE_LINK_TYPE, value,
|
144
|
+
def issue_link(value, link_pattern)
|
145
|
+
Link.new(ISSUE_LINK_TYPE, value, url(value, link_pattern))
|
132
146
|
end
|
133
147
|
|
134
148
|
# Get status based on exception type
|
135
149
|
# @param [Exception] exception
|
136
150
|
# @return [Symbol]
|
137
151
|
def status(exception)
|
138
|
-
|
152
|
+
exception.is_a?(RSpec::Expectations::ExpectationNotMetError) ? Status::FAILED : Status::BROKEN
|
139
153
|
end
|
140
154
|
|
141
155
|
# Get exception status detail
|
@@ -145,19 +159,20 @@ module Allure
|
|
145
159
|
StatusDetails.new(message: exception&.message, trace: exception&.backtrace&.join("\n"))
|
146
160
|
end
|
147
161
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
162
|
+
# Allure attachment object
|
163
|
+
# @param [String] name
|
164
|
+
# @param [String] type
|
165
|
+
# @return [Allure::Attachment]
|
166
|
+
def prepare_attachment(name, type)
|
167
|
+
extension = ContentType.to_extension(type) || return
|
168
|
+
file_name = "#{UUID.generate}-attachment.#{extension}"
|
169
|
+
Attachment.new(name: name, source: file_name, type: type)
|
152
170
|
end
|
153
171
|
|
154
|
-
|
155
|
-
Allure.configuration.link_issue_pattern.sub("{}", value)
|
156
|
-
end
|
172
|
+
private
|
157
173
|
|
158
|
-
def
|
159
|
-
|
160
|
-
exception.instance_of?(RSpec::Expectations::MultipleExpectationsNotMetError)
|
174
|
+
def url(value, link_pattern)
|
175
|
+
link_pattern.sub("{}", value)
|
161
176
|
end
|
162
177
|
end
|
163
178
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Adds support for annotating methods as allure steps
|
4
|
+
#
|
5
|
+
module AllureStepAnnotation
|
6
|
+
# Mark method definition as allure step
|
7
|
+
#
|
8
|
+
# @param [String] step_name
|
9
|
+
# @return [void]
|
10
|
+
def step(step_name = "")
|
11
|
+
@allure_step = step_name
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def singleton_method_added(method_name)
|
17
|
+
return super unless @allure_step
|
18
|
+
|
19
|
+
original_method = singleton_method(method_name)
|
20
|
+
step_name = @allure_step.empty? ? method_name.to_s : @allure_step
|
21
|
+
@allure_step = nil
|
22
|
+
|
23
|
+
define_singleton_method(method_name) do |*args, &block|
|
24
|
+
Allure.run_step(step_name) { original_method.call(*args, &block) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_added(method_name)
|
29
|
+
return super unless @allure_step
|
30
|
+
|
31
|
+
original_method = instance_method(method_name)
|
32
|
+
step_name = @allure_step.empty? ? method_name.to_s : @allure_step
|
33
|
+
@allure_step = nil
|
34
|
+
|
35
|
+
define_method(method_name) do |*args, &block|
|
36
|
+
Allure.run_step(step_name) { original_method.bind(self).call(*args, &block) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Allure
|
4
|
+
class TestPlan
|
5
|
+
# @return [String] test plan path env var name
|
6
|
+
TESTPLAN_PATH = "ALLURE_TESTPLAN_PATH"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Allure id's of executable tests
|
10
|
+
#
|
11
|
+
# @return [Array]
|
12
|
+
def test_ids
|
13
|
+
@test_ids ||= tests&.map { |test| test[:id] }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Test names of executable tests
|
17
|
+
#
|
18
|
+
# @return [Array]
|
19
|
+
def test_names
|
20
|
+
@test_names ||= tests&.map { |test| test[:selector] }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Tests to execute from allure testplan.json
|
26
|
+
#
|
27
|
+
# @return [Array<Hash>]
|
28
|
+
def tests
|
29
|
+
@tests ||= Oj.load_file(ENV[TESTPLAN_PATH], symbol_keys: true)&.fetch(:tests) if ENV[TESTPLAN_PATH]
|
30
|
+
rescue Oj::ParseError
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|