allure-ruby-commons 2.10.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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: []