allure-ruby-commons 2.10.0.beta2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c6ec9c5dd1a69617de027bd146f0deaf35ef51a1bd8861e013c41550070020b7
4
+ data.tar.gz: e049c7285aed91e6c8aceb3185ed4a11f143aaafb5b8ae20acdf996c1239f923
5
+ SHA512:
6
+ metadata.gz: 1367a4de864e0c704ac5223b626a8027bdb942aaa91ceac3e25b84ec228b86e6b4f4204c3439b55f8f8a9d5860e3b044877da910aa70270a19d1ee5a4f9be5f0
7
+ data.tar.gz: 626e8aa40a5d8f7cde655ca3b6a7f1b283a5006d80676aa86001e63986f25e674220cfabaff63d8f607a6381f9221e493cbed4d88e1f4833da6e570637b343f5
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Allure Ruby Adaptor API
2
+
3
+ This is a helper library containing the basics for any ruby-based Allure adaptor.
4
+ Using it you can easily implement the adaptor for your favorite ruby testing library or
5
+ you can just create the report of any other kind using the basic Allure terms.
6
+
7
+ ## Setup
8
+
9
+ Add the dependency to your Gemfile
10
+
11
+ ```ruby
12
+ gem 'allure-ruby-commons'
13
+ ```
14
+
15
+ ## Configuration
16
+
17
+ Following configuration options are supported:
18
+
19
+ ```ruby
20
+ Allure.configure do |c|
21
+ c.results_directory = "/whatever/you/like"
22
+ c.logging_level = Logger::INFO
23
+ # these are used for creating links to bugs or test cases where {} is replaced with keys of relevant items
24
+ c.link_tms_pattern = "http://www.jira.com/browse/{}"
25
+ c.link_issue_pattern = "http://www.jira.com/browse/{}"
26
+ end
27
+ ```
28
+
29
+ Getting the configuration object:
30
+
31
+ ```ruby
32
+ Allure.configuration
33
+ ```
34
+
35
+ ## Allure lifecycle
36
+
37
+ Reports are built using API defined in AllureLifecycle class and using allure specific entities defined in models.
38
+ Example of building a simple test case can be seen in [integration spec](spec/integration/full_report_spec.rb).
39
+
40
+ Convenience method `Allure.lifecycle` exists for getting thread specific allure lifecycle instance.
41
+
42
+ Additional methods in [Allure](lib/allure-ruby-commons.rb) exist to add various custom attributes to test report.
43
+
44
+ ```ruby
45
+ Allure.add_attachment(name: "attachment", source: "Some string", type: Allure::ContentType::TXT, test_case: false)
46
+ Allure.add_link(name: "Custom Url", url: "http://www.github.com")
47
+ ```
48
+
49
+ ## Testing
50
+
51
+ Install dependencies:
52
+
53
+ ```bash
54
+ bundle install
55
+ ```
56
+
57
+ Run tests:
58
+
59
+ ```bash
60
+ bundle exec rspec
61
+ ```
62
+
63
+ ## Building
64
+
65
+ ```bash
66
+ gem build allure-ruby-commons.gemspec
67
+ ```
@@ -0,0 +1,168 @@
1
+ # rubocop:disable Naming/FileName
2
+ # frozen_string_literal: true
3
+
4
+ require "require_all"
5
+ require "uuid"
6
+ require_rel "allure_ruby_commons/**/*rb"
7
+
8
+ # Namespace for classes that handle allure report generation and different framework adaptors
9
+ module Allure
10
+ class << self
11
+ # Get thread specific allure lifecycle object
12
+ # @return [Allure::AllureLifecycle]
13
+ def lifecycle
14
+ Thread.current[:lifecycle] ||= AllureLifecycle.new
15
+ end
16
+
17
+ # Set lifecycle object
18
+ # @param [Allure::AllureLifecycle] lifecycle
19
+ # @return [void]
20
+ def lifecycle=(lifecycle)
21
+ Thread.current[:lifecycle] = lifecycle
22
+ end
23
+
24
+ # Get allure configuration
25
+ # @return [Allure::Config]
26
+ def configuration
27
+ Config
28
+ end
29
+
30
+ # Set allure configuration
31
+ # @yieldparam [Allure::Config]
32
+ # @yieldreturn [void]
33
+ # @return [void]
34
+ def configure
35
+ yield(Config)
36
+ end
37
+
38
+ # Add epic to current test case
39
+ # @param [String] value
40
+ # @return [void]
41
+ def epic(value)
42
+ label(ResultUtils::EPIC_LABEL_NAME, value)
43
+ end
44
+
45
+ # Add feature to current test case
46
+ # @param [String] value
47
+ # @return [void]
48
+ def feature(value)
49
+ label(ResultUtils::FEATURE_LABEL_NAME, value)
50
+ end
51
+
52
+ # Add story to current test case
53
+ # @param [String] value
54
+ # @return [void]
55
+ def story(value)
56
+ label(ResultUtils::STORY_LABEL_NAME, value)
57
+ end
58
+
59
+ # Add suite to current test case
60
+ # @param [String] value
61
+ # @return [void]
62
+ def suite(value)
63
+ label(ResultUtils::SUITE_LABEL_NAME, value)
64
+ end
65
+
66
+ # Add label to current test case
67
+ # @param [String] name
68
+ # @param [String] value
69
+ # @return [void]
70
+ def label(name, value)
71
+ lifecycle.update_test_case do |test_case|
72
+ test_case.labels.push(Label.new(name, value))
73
+ end
74
+ end
75
+
76
+ # Add description to current test case
77
+ # @param [String] description
78
+ # @return [void]
79
+ def description(description)
80
+ lifecycle.update_test_case do |test_case|
81
+ test_case.description = description
82
+ end
83
+ end
84
+
85
+ # Add html description to current test case
86
+ # @param [String] description_html
87
+ # @return [void]
88
+ def description_html(description_html)
89
+ lifecycle.update_test_case do |test_case|
90
+ test_case.description_html = description_html
91
+ end
92
+ end
93
+
94
+ # Add parameter to current test case
95
+ # @param [String] name
96
+ # @param [String] value
97
+ # @return [void]
98
+ def parameter(name, value)
99
+ lifecycle.update_test_case do |test_case|
100
+ test_case.parameters.push(Parameter.new(name, value))
101
+ end
102
+ end
103
+
104
+ # Add tms link to current test case
105
+ # @param [String] name
106
+ # @param [String] url
107
+ # @return [void]
108
+ def tms(name, url)
109
+ add_link(name: name, url: url, type: ResultUtils::TMS_LINK_TYPE)
110
+ end
111
+
112
+ # Add issue linkt to current test case
113
+ # @param [String] name
114
+ # @param [String] url
115
+ # @return [void]
116
+ def issue(name, url)
117
+ add_link(name: name, url: url, type: ResultUtils::ISSUE_LINK_TYPE)
118
+ end
119
+
120
+ # Add link to current test case
121
+ # @param [String ] url
122
+ # @param [String] name
123
+ # @param [String] type type of the link used to display link icon
124
+ # @return [void]
125
+ def add_link(url:, name: nil, type: "custom")
126
+ lifecycle.update_test_case do |test_case|
127
+ test_case.links.push(Link.new(type, name || url, url))
128
+ end
129
+ end
130
+
131
+ # Add attachment to current test case or step
132
+ # @param [String] name Attachment name
133
+ # @param [File, String] source File or string to save as attachment
134
+ # @param [String] type attachment type defined in {Allure::ContentType}
135
+ # @param [Boolean] test_case add attachment to current test case instead of test step
136
+ # @return [void]
137
+ def add_attachment(name:, source:, type:, test_case: false)
138
+ lifecycle.add_attachment(name: name, source: source, type: type, test_case: test_case)
139
+ end
140
+
141
+ # Add step with provided name and optional status to current test step, fixture or test case
142
+ # @param [String] name
143
+ # @param [Symbol] status <Allure::Status>, <Allure::Status::PASSED> by default
144
+ # @return [void]
145
+ def step(name:, status: nil)
146
+ lifecycle.add_test_step(StepResult.new(name: name, status: status || Status::PASSED, stage: Stage::FINISHED))
147
+ end
148
+
149
+ # Run passed block as step with given name
150
+ # @param [String] name
151
+ # @yield [] step block
152
+ # @return [void]
153
+ def run_step(name)
154
+ lifecycle.start_test_step(StepResult.new(name: name, stage: Stage::RUNNING))
155
+ yield
156
+ lifecycle.update_test_step { |step| step.status = Status::PASSED }
157
+ rescue => e
158
+ lifecycle.update_test_step do |step|
159
+ step.status = ResultUtils.status(e)
160
+ step.status_details = ResultUtils.status_details(e)
161
+ end
162
+ raise(e)
163
+ ensure
164
+ lifecycle.stop_test_step
165
+ end
166
+ end
167
+ end
168
+ # rubocop:enable Naming/FileName
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ # Main class for creating and writing allure results
5
+ class AllureLifecycle
6
+ def initialize
7
+ @step_context = []
8
+ end
9
+
10
+ # Start test result container
11
+ # @param [Allure::TestResultContainer] test_result_container
12
+ # @return [Allure::TestResultContainer]
13
+ def start_test_container(test_result_container)
14
+ test_result_container.start = ResultUtils.timestamp
15
+ @current_test_result_container = test_result_container
16
+ end
17
+
18
+ # @example Update current test container
19
+ # update_test_container do |container|
20
+ # container.stage = Allure::Stage::FINISHED
21
+ # end
22
+ # @yieldparam [Allure::TestResultContainer] current test result container
23
+ # @yieldreturn [void]
24
+ # @return [void]
25
+ def update_test_container
26
+ unless @current_test_result_container
27
+ return logger.error("Could not update test container, no container is running.")
28
+ end
29
+
30
+ yield(@current_test_result_container)
31
+ end
32
+
33
+ # Stop current test container and write result
34
+ # @return [void]
35
+ def stop_test_container
36
+ unless @current_test_result_container
37
+ return logger.error("Could not stop test container, no container is running.")
38
+ end
39
+
40
+ @current_test_result_container.stop = ResultUtils.timestamp
41
+ file_writer.write_test_result_container(@current_test_result_container)
42
+ clear_current_test_container
43
+ end
44
+
45
+ # Start test case and add to current test container
46
+ # @param [Allure::TestResult] test_result
47
+ # @return [Allure::TestResult]
48
+ def start_test_case(test_result)
49
+ clear_step_context
50
+ unless @current_test_result_container
51
+ return logger.error("Could not start test case, test container is not started")
52
+ end
53
+
54
+ test_result.start = ResultUtils.timestamp
55
+ test_result.stage = Stage::RUNNING
56
+ test_result.labels.push(ResultUtils.thread_label, ResultUtils.host_label, ResultUtils.language_label)
57
+ @current_test_result_container.children.push(test_result.uuid)
58
+ @current_test_case = test_result
59
+ end
60
+
61
+ # @example Update current test case
62
+ # update_test_container do |test_case|
63
+ # test_case.status = Allure::Status::FAILED
64
+ # end
65
+ # @yieldparam [Allure::TestResult] current test
66
+ # @yieldreturn [void]
67
+ # @return [void]
68
+ def update_test_case
69
+ return logger.error("Could not update test case, no test case running") unless @current_test_case
70
+
71
+ yield(@current_test_case)
72
+ end
73
+
74
+ # Stop current test case and write result
75
+ # @return [void]
76
+ def stop_test_case
77
+ return logger.error("Could not stop test case, no test case is running") unless @current_test_case
78
+
79
+ @current_test_case.stop = ResultUtils.timestamp
80
+ @current_test_case.stage = Stage::FINISHED
81
+ file_writer.write_test_result(@current_test_case)
82
+ clear_current_test_case
83
+ clear_step_context
84
+ end
85
+
86
+ # Start test step and add to current test case
87
+ # @param [Allure::StepResult] step_result
88
+ # @return [Allure::StepResult]
89
+ def start_test_step(step_result)
90
+ return logger.error("Could not start test step, no test case is running") unless @current_test_case
91
+
92
+ step_result.start = ResultUtils.timestamp
93
+ step_result.stage = Stage::RUNNING
94
+ add_test_step(step_result)
95
+ step_result
96
+ end
97
+
98
+ # @example Update current test step
99
+ # update_test_container do |test_step|
100
+ # test_step.status = Allure::Status::BROKEN
101
+ # end
102
+ # @yieldparam [Allure::StepResult] current test step
103
+ # @yieldreturn [void]
104
+ # @return [void]
105
+ def update_test_step
106
+ return logger.error("Could not update test step, no step is running") unless current_test_step
107
+
108
+ yield(current_test_step)
109
+ end
110
+
111
+ # Stop current test step
112
+ # @return [void]
113
+ def stop_test_step
114
+ return logger.error("Could not stop test step, no step is running") unless current_test_step
115
+
116
+ current_test_step.stop = ResultUtils.timestamp
117
+ current_test_step.stage = Stage::FINISHED
118
+ clear_last_test_step
119
+ end
120
+
121
+ # Start prepare fixture
122
+ # @param [Allure::FixtureResult] fixture_result
123
+ # @return [Allure::FixtureResult]
124
+ def start_prepare_fixture(fixture_result)
125
+ start_fixture(fixture_result) || return
126
+ @current_test_result_container.befores.push(fixture_result)
127
+ @current_fixture = fixture_result
128
+ end
129
+
130
+ # Start tear down fixture
131
+ # @param [Allure::FixtureResult] fixture_result
132
+ # @return [Allure::FixtureResult]
133
+ def start_tear_down_fixture(fixture_result)
134
+ start_fixture(fixture_result) || return
135
+ @current_test_result_container.afters.push(fixture_result)
136
+ @current_fixture = fixture_result
137
+ end
138
+
139
+ # Start fixture
140
+ # @param [Allure::FixtureResult] fixture_result
141
+ # @return [Allure::FixtureResult]
142
+ def start_fixture(fixture_result)
143
+ clear_step_context
144
+ unless @current_test_result_container
145
+ logger.error("Could not start fixture, test container is not started")
146
+ return false
147
+ end
148
+
149
+ fixture_result.start = ResultUtils.timestamp
150
+ fixture_result.stage = Stage::RUNNING
151
+ end
152
+
153
+ # @example Update current fixture
154
+ # update_test_container do |fixture|
155
+ # fixture.status = Allure::Status::BROKEN
156
+ # end
157
+ # @yieldparam [Allure::FixtureResult] current fixture
158
+ # @yieldreturn [void]
159
+ # @return [void]
160
+ def update_fixture
161
+ return logger.error("Could not update fixture, fixture is not started") unless @current_fixture
162
+
163
+ yield(@current_fixture)
164
+ end
165
+
166
+ def stop_fixture
167
+ return logger.error("Could not stop fixture, fixture is not started") unless @current_fixture
168
+
169
+ @current_fixture.stop = ResultUtils.timestamp
170
+ @current_fixture.stage = Stage::FINISHED
171
+ clear_current_fixture
172
+ clear_step_context
173
+ end
174
+
175
+ # Add attachment to current test or step
176
+ # @param [String] name Attachment name
177
+ # @param [File, String] source File or string to save as attachment
178
+ # @param [String] type attachment type defined in {Allure::ContentType}
179
+ # @param [Boolean] test_case add attachment to current test case
180
+ # @return [void]
181
+ def add_attachment(name:, source:, type:, test_case: false)
182
+ attachment = prepare_attachment(name, type) || begin
183
+ return logger.error("Can't add attachment, unrecognized mime type: #{type}")
184
+ end
185
+ executable_item = (test_case && @current_test_case) || current_executable
186
+ executable_item&.attachments&.push(attachment) || begin
187
+ return logger.error("Can't add attachment, no test, step or fixture is running")
188
+ end
189
+ write_attachment(source, attachment)
190
+ end
191
+
192
+ # Create attachment object
193
+ # @param [String] name
194
+ # @param [String] type
195
+ # @return [Allure::Attachment]
196
+ def prepare_attachment(name, type)
197
+ extension = ContentType.to_extension(type) || return
198
+ file_name = "#{UUID.generate}-attachment.#{extension}"
199
+ Attachment.new(name: name, source: file_name, type: type)
200
+ end
201
+
202
+ # Write attachment file
203
+ # @param [File, String] source
204
+ # @param [Allure::Attachment] attachment
205
+ # @return [void]
206
+ def write_attachment(source, attachment)
207
+ file_writer.write_attachment(source, attachment)
208
+ end
209
+
210
+ # Add step to current fixture|step|test case
211
+ # @param [Allure::StepResult] step_result
212
+ # @return [Allure::StepResult]
213
+ def add_test_step(step_result)
214
+ current_executable.steps.push(step_result)
215
+ @step_context.push(step_result)
216
+ step_result
217
+ end
218
+
219
+ private
220
+
221
+ def logger
222
+ @logger ||= Logger.new(STDOUT, level: Config.logging_level)
223
+ end
224
+
225
+ def file_writer
226
+ @file_writer ||= FileWriter.new
227
+ end
228
+
229
+ def current_executable
230
+ current_test_step || @current_fixture || @current_test_case
231
+ end
232
+
233
+ def clear_current_test_container
234
+ @current_test_result_container = nil
235
+ end
236
+
237
+ def clear_current_test_case
238
+ @current_test_case = nil
239
+ end
240
+
241
+ def current_test_step
242
+ @step_context.last
243
+ end
244
+
245
+ def clear_last_test_step
246
+ @step_context.pop
247
+ end
248
+
249
+ def clear_step_context
250
+ @step_context.clear
251
+ end
252
+
253
+ def clear_current_fixture
254
+ @current_fixture = nil
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module Allure
6
+ class Config
7
+ DEFAULT_RESULTS_DIRECTORY = "reports/allure-results"
8
+ DEFAULT_LOGGING_LEVEL = Logger::INFO
9
+
10
+ class << self
11
+ attr_accessor :link_tms_pattern, :link_issue_pattern
12
+ attr_writer :results_directory, :logging_level
13
+
14
+ def results_directory
15
+ @results_directory || DEFAULT_RESULTS_DIRECTORY
16
+ end
17
+
18
+ def logging_level
19
+ @logging_level || DEFAULT_LOGGING_LEVEL
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class FileWriter
5
+ TEST_RESULT_SUFFIX = "-result.json"
6
+ TEST_RESULT_CONTAINER_SUFFIX = "-container.json"
7
+ ATTACHMENT_FILE_SUFFIX = "-attachment"
8
+
9
+ # Write test result
10
+ # @param [Allure::TestResult] test_result
11
+ # @return [void]
12
+ def write_test_result(test_result)
13
+ write("#{test_result.uuid}#{TEST_RESULT_SUFFIX}", test_result.to_json)
14
+ end
15
+
16
+ # Write test result container
17
+ # @param [Allure::TestResultContainer] test_container_result
18
+ # @return [void]
19
+ def write_test_result_container(test_container_result)
20
+ write("#{test_container_result.uuid}#{TEST_RESULT_CONTAINER_SUFFIX}", test_container_result.to_json)
21
+ end
22
+
23
+ # Write allure attachment file
24
+ # @param [File, String] source File or string of attachment to save
25
+ # @param [Allure::Attachment] attachment
26
+ # @return [void]
27
+ def write_attachment(source, attachment)
28
+ source.is_a?(File) ? copy(source.path, attachment.source) : write(attachment.source, source)
29
+ end
30
+
31
+ private
32
+
33
+ def output_dir
34
+ @output_dir ||= FileUtils.mkpath(Allure::Config.results_directory).first
35
+ end
36
+
37
+ def write(name, source)
38
+ filename = File.join(output_dir, name)
39
+ File.open(filename, "w") { |file| file.write(source) }
40
+ end
41
+
42
+ def copy(from, to)
43
+ FileUtils.cp(from, File.join(output_dir, to))
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+
5
+ module Allure
6
+ class Attachment < JSONable
7
+ # @param [String] name attachment name
8
+ # @param [String] type attachment type, {Allure::ContentType}
9
+ # @param [String] source attachment file name
10
+ def initialize(name:, type:, source:)
11
+ @name = name
12
+ @type = type
13
+ @source = source
14
+ end
15
+
16
+ attr_accessor :name, :type, :source
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class ContentType
5
+ TXT = "text/plain"
6
+ XML = "application/xml"
7
+ CSV = "text/csv"
8
+ TSV = "text/tab-separated-values"
9
+ CSS = "text/css"
10
+ URI = "text/uri-list"
11
+ SVG = "image/svg+xml"
12
+ PNG = "image/png"
13
+ JSON = "application/json"
14
+ WEBM = "video/webm"
15
+ JPG = "image/jpeg"
16
+
17
+ def self.to_extension(content_type)
18
+ constants.detect { |const| const_get(const) == content_type }&.to_s&.downcase
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+
5
+ module Allure
6
+ class ExecutableItem < JSONable
7
+ # @param [Hash] options
8
+ # @option options [String] :name
9
+ # @option options [String] :description
10
+ # @option options [String] :description_html
11
+ # @option options [String] :stage ('scheduled'), {Allure::Stage}
12
+ # @option options [String] :status ('broken'), {Allure::Status}
13
+ # @option options [Allure::StatusDetails] :status_details
14
+ # @option options [Array<Allure::ExecutableItem>] :steps ([])
15
+ # @option options [Array<Allure::Attachment>] :attachments ([])
16
+ # @option options [Array<Allure::Parameter>] :parameters ([])
17
+ def initialize(**options)
18
+ @name = options[:name]
19
+ @description = options[:description]
20
+ @description_html = options[:description_html]
21
+ @status = options[:status] || Status::BROKEN
22
+ @status_details = options[:status_details] || StatusDetails.new
23
+ @stage = options[:stage] || Stage::SCHEDULED
24
+ @steps = options[:steps] || []
25
+ @attachments = options[:attachments] || []
26
+ @parameters = options[:parameters] || []
27
+ end
28
+
29
+ attr_accessor(
30
+ :name, :status, :status_details, :stage, :description, :description_html,
31
+ :steps, :attachments, :parameters, :start, :stop
32
+ )
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class FixtureResult < ExecutableItem; end
5
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Allure
6
+ class JSONable
7
+ def as_json(_options = {})
8
+ instance_variables.each_with_object({}) do |var, map|
9
+ key = camelcase(var.to_s.delete_prefix("@"))
10
+ value = instance_variable_get(var)
11
+ map[key] = value unless value.nil?
12
+ end
13
+ end
14
+
15
+ def to_json(*options)
16
+ as_json(*options).to_json(*options)
17
+ end
18
+
19
+ def ==(other)
20
+ self.class == other.class && state == other.state
21
+ end
22
+
23
+ protected
24
+
25
+ def state
26
+ instance_variables.map { |var| instance_variable_get(var) }
27
+ end
28
+
29
+ private
30
+
31
+ def camelcase(str)
32
+ str = str.gsub(/(?:_+)([a-z])/) { Regexp.last_match(1).upcase }
33
+ str.gsub(/(\A|\s)([A-Z])/) { Regexp.last_match(1) + Regexp.last_match(2).downcase }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+
5
+ module Allure
6
+ class Label < JSONable
7
+ def initialize(name, value)
8
+ @name = name
9
+ @value = value
10
+ end
11
+
12
+ attr_accessor :name, :value
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+
5
+ module Allure
6
+ class Link < JSONable
7
+ def initialize(type, name, url)
8
+ @type = type
9
+ @name = name
10
+ @url = url
11
+ end
12
+
13
+ attr_accessor :type, :name, :url
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+
5
+ module Allure
6
+ class Parameter < JSONable
7
+ def initialize(name, value)
8
+ @name = name
9
+ @value = value
10
+ end
11
+
12
+ attr_reader :name, :value
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class Stage
5
+ SCHEDULED = "scheduled"
6
+ RUNNING = "running"
7
+ FINISHED = "finished"
8
+ INTERRUPTED = "interrupted"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class Status
5
+ FAILED = :failed
6
+ BROKEN = :broken
7
+ PASSED = :passed
8
+ SKIPPED = :skipped
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class StatusDetails < JSONable
5
+ # @param [Boolean] known
6
+ # @param [Boolean] muted
7
+ # @param [Boolean] flaky
8
+ # @param [String] message
9
+ # @param [String] trace
10
+ def initialize(known: false, muted: false, flaky: false, message: nil, trace: nil)
11
+ @known = known
12
+ @muted = muted
13
+ @flaky = flaky
14
+ @message = message
15
+ @trace = trace
16
+ end
17
+
18
+ attr_accessor :known, :muted, :flaky, :message, :trace
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class StepResult < ExecutableItem; end
5
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Allure
4
+ class TestResult < ExecutableItem
5
+ # @param [String] uuid
6
+ # @param [String] history_id
7
+ # @param [Hash] options
8
+ # @option options [String] :name
9
+ # @option options [String] :full_name
10
+ # @option options [String] :description
11
+ # @option options [String] :description_html
12
+ # @option options [String] :status ('broken')
13
+ # @option options [String] :stage ('scheduled')
14
+ # @option options [Allure::StatusDetails] :status_details
15
+ # @option options [Array<Allure::ExecutableItem>] :steps ([])
16
+ # @option options [Array<Allure::Label>] :labels ([])
17
+ # @option options [Array<Allure::Link>] :links ([])
18
+ # @option options [Array<Allure::Attachment>] :attachments ([])
19
+ # @option options [Array<Allure::Parameter>] :parameters ([])
20
+ def initialize(uuid: UUID.generate, history_id: UUID.generate, **options)
21
+ super
22
+ @uuid = uuid
23
+ @history_id = history_id
24
+ @full_name = options[:full_name] || "Unnamed"
25
+ @labels = options[:labels] || []
26
+ @links = options[:links] || []
27
+ end
28
+
29
+ attr_accessor :uuid, :history_id, :full_name, :labels, :links
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jsonable"
4
+ module Allure
5
+ class TestResultContainer < JSONable
6
+ def initialize(uuid: UUID.generate, name: "Unnamed")
7
+ @uuid = uuid
8
+ @name = name
9
+ @children = []
10
+ @befores = []
11
+ @afters = []
12
+ @links = []
13
+ end
14
+
15
+ attr_accessor :uuid, :name, :description, :description_html, :start, :stop, :children, :befores, :afters, :links
16
+ end
17
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+
5
+ module Allure
6
+ module ResultUtils
7
+ ISSUE_LINK_TYPE = "issue"
8
+ TMS_LINK_TYPE = "tms"
9
+
10
+ ALLURE_ID_LABEL_NAME = "AS_ID"
11
+ SUITE_LABEL_NAME = "suite"
12
+ PARENT_SUITE_LABEL_NAME = "parentSuite"
13
+ SUB_SUITE_LABEL_NAME = "subSuite"
14
+ EPIC_LABEL_NAME = "epic"
15
+ FEATURE_LABEL_NAME = "feature"
16
+ STORY_LABEL_NAME = "story"
17
+ SEVERITY_LABEL_NAME = "severity"
18
+ TAG_LABEL_NAME = "tag"
19
+ OWNER_LABEL_NAME = "owner"
20
+ LEAD_LABEL_NAME = "lead"
21
+ HOST_LABEL_NAME = "host"
22
+ THREAD_LABEL_NAME = "thread"
23
+ TEST_METHOD_LABEL_NAME = "testMethod"
24
+ TEST_CLASS_LABEL_NAME = "testClass"
25
+ PACKAGE_LABEL_NAME = "package"
26
+ FRAMEWORK_LABEL_NAME = "framework"
27
+ LANGUAGE_LABEL_NAME = "language"
28
+
29
+ class << self
30
+ # @param [Time] time
31
+ # @return [Number]
32
+ def timestamp(time = nil)
33
+ ((time || Time.now).to_f * 1000).to_i
34
+ end
35
+
36
+ # Current thread label
37
+ # @return [Allure::Label]
38
+ def thread_label
39
+ Label.new(THREAD_LABEL_NAME, Thread.current.object_id)
40
+ end
41
+
42
+ # Host label
43
+ # @return [Allure::Label]
44
+ def host_label
45
+ Label.new(HOST_LABEL_NAME, Socket.gethostname)
46
+ end
47
+
48
+ def language_label
49
+ Label.new(LANGUAGE_LABEL_NAME, "ruby")
50
+ end
51
+
52
+ def framework_label(value)
53
+ Label.new(FRAMEWORK_LABEL_NAME, value)
54
+ end
55
+
56
+ # Feature label
57
+ # @param [String] value
58
+ # @return [Allure::Label]
59
+ def feature_label(value)
60
+ Label.new(FEATURE_LABEL_NAME, value)
61
+ end
62
+
63
+ # Package label
64
+ # @param [String] value
65
+ # @return [Allure::Label]
66
+ def package_label(value)
67
+ Label.new(PACKAGE_LABEL_NAME, value)
68
+ end
69
+
70
+ # Suite label
71
+ # @param [String] value
72
+ # @return [Allure::Label]
73
+ def suite_label(value)
74
+ Label.new(SUITE_LABEL_NAME, value)
75
+ end
76
+
77
+ # Story label
78
+ # @param [String] value
79
+ # @return [Allure::Label]
80
+ def story_label(value)
81
+ Label.new(STORY_LABEL_NAME, value)
82
+ end
83
+
84
+ # Test case label
85
+ # @param [String] value
86
+ # @return [Allure::Label]
87
+ def test_class_label(value)
88
+ Label.new(TEST_CLASS_LABEL_NAME, value)
89
+ end
90
+
91
+ # Tag label
92
+ # @param [String] value
93
+ # @return [Allure::Label]
94
+ def tag_label(value)
95
+ Label.new(TAG_LABEL_NAME, value)
96
+ end
97
+
98
+ # Severity label
99
+ # @param [String] value
100
+ # @return [Allure::Label]
101
+ def severity_label(value)
102
+ Label.new(SEVERITY_LABEL_NAME, value)
103
+ end
104
+
105
+ # TMS link
106
+ # @param [String] value
107
+ # @return [Allure::Link]
108
+ def tms_link(value)
109
+ Link.new(TMS_LINK_TYPE, value, tms_url(value))
110
+ end
111
+
112
+ # Issue link
113
+ # @param [String] value
114
+ # @return [Allure::Link]
115
+ def issue_link(value)
116
+ Link.new(ISSUE_LINK_TYPE, value, issue_url(value))
117
+ end
118
+
119
+ # Get status based on exception type
120
+ # @param [Exception] exception
121
+ # @return [String]
122
+ def status(exception)
123
+ expectation_error?(exception) ? Status::FAILED : Status::BROKEN
124
+ end
125
+
126
+ # Get exception status detail
127
+ # @param [Exception] exception
128
+ # @return [Hash<Symbol, String>]
129
+ def status_details(exception)
130
+ StatusDetails.new(message: exception.message, trace: exception.backtrace&.join("\n"))
131
+ end
132
+
133
+ private
134
+
135
+ def tms_url(value)
136
+ Allure.configuration.link_tms_pattern.sub("{}", value)
137
+ end
138
+
139
+ def issue_url(value)
140
+ Allure.configuration.link_issue_pattern.sub("{}", value)
141
+ end
142
+
143
+ def expectation_error?(exception)
144
+ exception.instance_of?(RSpec::Expectations::ExpectationNotMetError) ||
145
+ exception.instance_of?(RSpec::Expectations::MultipleExpectationsNotMetError)
146
+ rescue NameError
147
+ false
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open-uri"
4
+ require "zip"
5
+ require "pathname"
6
+
7
+ module Allure
8
+ class Util
9
+ ALLURE_CLI_VERSION = "2.10.0"
10
+ ALLURE_BIN_URL = "http://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/"\
11
+ "#{ALLURE_CLI_VERSION}/allure-commandline-#{ALLURE_CLI_VERSION}.zip"
12
+
13
+ class << self
14
+ # Download allure bin if appropriate version is not in path
15
+ # @return [String] allure executable
16
+ def allure_cli
17
+ return "allure" if ALLURE_CLI_VERSION == `allure --version`.chomp
18
+
19
+ cli_dir = File.join(".allure", "allure-#{ALLURE_CLI_VERSION}")
20
+ zip = File.join(".allure", "allure.zip")
21
+ bin = File.join(cli_dir, "bin", "allure")
22
+
23
+ FileUtils.mkpath(".allure")
24
+ download_allure(zip) unless File.exist?(zip) || File.exist?(bin)
25
+ extract_allure(zip, ".allure") unless File.exist?(bin)
26
+
27
+ bin
28
+ end
29
+
30
+ private
31
+
32
+ def download_allure(destination)
33
+ File.open(destination, "w") { |file| file.write(open(ALLURE_BIN_URL).read) } # rubocop:disable Security/Open
34
+ end
35
+
36
+ def extract_allure(zip, destination)
37
+ Zip::File.foreach(zip) do |entry|
38
+ entry.restore_permissions = true
39
+ entry.extract(File.join(destination, entry.name))
40
+ end
41
+ FileUtils.rm(zip)
42
+ end
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: allure-ruby-commons
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.10.0.beta2
5
+ platform: ruby
6
+ authors:
7
+ - Andrejs Cunskis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: uuid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.3.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.3.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: require_all
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '3'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '1.8'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '3'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubyzip
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.2'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.2'
75
+ description: Utilities allowing to implement allure result generation by other test
76
+ frameworks
77
+ email: andrejs.cunskis@gmail.com
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - README.md
83
+ - lib/allure-ruby-commons.rb
84
+ - lib/allure_ruby_commons/allure_lifecycle.rb
85
+ - lib/allure_ruby_commons/config.rb
86
+ - lib/allure_ruby_commons/file_writer.rb
87
+ - lib/allure_ruby_commons/model/attachment.rb
88
+ - lib/allure_ruby_commons/model/content_type.rb
89
+ - lib/allure_ruby_commons/model/executable_item.rb
90
+ - lib/allure_ruby_commons/model/fixture_result.rb
91
+ - lib/allure_ruby_commons/model/jsonable.rb
92
+ - lib/allure_ruby_commons/model/label.rb
93
+ - lib/allure_ruby_commons/model/link.rb
94
+ - lib/allure_ruby_commons/model/parameter.rb
95
+ - lib/allure_ruby_commons/model/stage.rb
96
+ - lib/allure_ruby_commons/model/status.rb
97
+ - lib/allure_ruby_commons/model/status_details.rb
98
+ - lib/allure_ruby_commons/model/step_result.rb
99
+ - lib/allure_ruby_commons/model/test_result.rb
100
+ - lib/allure_ruby_commons/model/test_result_container.rb
101
+ - lib/allure_ruby_commons/result_utils.rb
102
+ - lib/allure_ruby_commons/util.rb
103
+ homepage: http://allure.qatools.ru
104
+ licenses:
105
+ - Apache-2.0
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 2.5.0
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">"
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ requirements: []
122
+ rubygems_version: 3.0.3
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: Common library for allure results generation
126
+ test_files: []