rspec_junit_formatter 0.2.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/LICENSE +1 -1
- data/README.md +93 -21
- data/lib/rspec_junit_formatter/rspec2.rb +41 -9
- data/lib/rspec_junit_formatter/rspec3.rb +94 -6
- data/lib/rspec_junit_formatter.rb +157 -27
- data.tar.gz.sig +0 -0
- metadata +80 -31
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 903f156687c6fff260143332222e9950fca53b5306bcde7cf197c44f3af4acf7
|
4
|
+
data.tar.gz: 1d2ffca113b3cd78b3230d66ed41268609ce38f06e50da6cd196af9073670500
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8d32b7a3b5dec828fe29c714755b3698ad8a48b1cfc92185b6f53e5eb4a6ab47adae5cb74743cbc6b5814d0a5d61d15d49b632aadd5afdede74820628796687
|
7
|
+
data.tar.gz: 76537f5a365796f1e062cbfde157f8f48c163b7973ae6a1faae533cbf4684bd3c29cd26d6cb344eac8c46c0ae950f854b48f21bbf2bd28bfbf2ebab6e12b639e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,49 +1,121 @@
|
|
1
1
|
# RSpec JUnit Formatter
|
2
2
|
|
3
|
-
[![Build results](
|
4
|
-
[![Gem version](http://img.shields.io/gem/v/rspec_junit_formatter.svg)](https://rubygems.org/
|
3
|
+
[![Build results](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sj26/rspec_junit_formatter/actions/workflows/ci.yml?branch=main)
|
4
|
+
[![Gem version](http://img.shields.io/gem/v/rspec_junit_formatter.svg)](https://rubygems.org/gems/rspec_junit_formatter)
|
5
5
|
|
6
|
-
[RSpec][rspec] 2 & 3 results that [Jenkins][jenkins]
|
6
|
+
[RSpec][rspec] 2 & 3 results that your CI can read. [Jenkins][jenkins-junit], [Buildkite][buildkite-junit], [CircleCI][circleci-junit], [Gitlab][gitlab-junit], and probably more, too.
|
7
7
|
|
8
|
-
|
8
|
+
[rspec]: http://rspec.info/
|
9
|
+
[jenkins-junit]: https://jenkins.io/doc/pipeline/steps/junit/
|
10
|
+
[buildkite-junit]: https://github.com/buildkite/rspec-junit-example
|
11
|
+
[circleci-junit]: https://circleci.com/docs/2.0/collect-test-data/
|
12
|
+
[gitlab-junit]: https://docs.gitlab.com/ee/ci/unit_test_reports.html#ruby-example
|
9
13
|
|
10
14
|
## Usage
|
11
15
|
|
12
16
|
Install the gem:
|
13
17
|
|
14
|
-
|
18
|
+
```sh
|
19
|
+
gem install rspec_junit_formatter
|
20
|
+
```
|
15
21
|
|
16
22
|
Use it:
|
17
23
|
|
18
|
-
|
24
|
+
```sh
|
25
|
+
rspec --format RspecJunitFormatter --out rspec.xml
|
26
|
+
```
|
27
|
+
|
28
|
+
You'll get an XML file `rspec.xml` with your results in it.
|
29
|
+
|
30
|
+
You can use it in combination with other [formatters][rspec-formatters], too:
|
19
31
|
|
20
|
-
|
32
|
+
```sh
|
33
|
+
rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
34
|
+
```
|
21
35
|
|
22
|
-
|
36
|
+
[rspec-formatters]: https://relishapp.com/rspec/rspec-core/v/3-6/docs/formatters
|
37
|
+
|
38
|
+
### Using in your project with Bundler
|
23
39
|
|
24
40
|
Add it to your Gemfile if you're using [Bundler][bundler]. Put it in the same groups as rspec.
|
25
41
|
|
26
|
-
|
42
|
+
```ruby
|
43
|
+
group :test do
|
44
|
+
gem "rspec"
|
45
|
+
gem "rspec_junit_formatter"
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Put the same arguments as the commands above in [your `.rspec`][rspec-file]:
|
50
|
+
|
51
|
+
```sh
|
52
|
+
--format RspecJunitFormatter
|
53
|
+
--out rspec.xml
|
54
|
+
```
|
55
|
+
[bundler]: https://bundler.io
|
56
|
+
[rspec-file]: https://relishapp.com/rspec/rspec-core/v/3-6/docs/configuration/read-command-line-configuration-options-from-files
|
57
|
+
|
58
|
+
### Parallel tests
|
59
|
+
|
60
|
+
For use with `parallel_tests`, add `$TEST_ENV_NUMBER` in the output file option (in `.rspec` or `.rspec_parallel`) to avoid concurrent process write conflicts.
|
61
|
+
|
62
|
+
```sh
|
63
|
+
--format RspecJunitFormatter
|
64
|
+
--out tmp/rspec<%= ENV["TEST_ENV_NUMBER"] %>.xml
|
65
|
+
```
|
66
|
+
|
67
|
+
The formatter includes `$TEST_ENV_NUMBER` in the test suite name within the XML, too.
|
68
|
+
|
69
|
+
### Capturing output
|
70
|
+
|
71
|
+
If you like, you can capture the standard output and error streams of each test into the `:stdout` and `:stderr` example metadata which will be added to the junit report, e.g.:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# spec_helper.rb
|
27
75
|
|
28
|
-
|
29
|
-
|
76
|
+
RSpec.configure do |config|
|
77
|
+
# register around filter that captures stdout and stderr
|
78
|
+
config.around(:each) do |example|
|
79
|
+
$stdout = StringIO.new
|
80
|
+
$stderr = StringIO.new
|
30
81
|
|
31
|
-
|
82
|
+
example.run
|
32
83
|
|
33
|
-
|
84
|
+
example.metadata[:stdout] = $stdout.string
|
85
|
+
example.metadata[:stderr] = $stderr.string
|
34
86
|
|
35
|
-
|
36
|
-
|
87
|
+
$stdout = STDOUT
|
88
|
+
$stderr = STDERR
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
Note that this example captures all output from every example all the time, potentially interfering with local debugging. You might like to restrict this to only on CI, or by using [rspec filters](https://relishapp.com/rspec/rspec-core/docs/hooks/filters).
|
94
|
+
|
95
|
+
## Caveats
|
96
|
+
|
97
|
+
* XML can only represent a [limited subset of characters][xml-charsets] which excludes null bytes and most control characters. This gem will use character entities where possible and fall back to replacing invalid characters with Ruby-like escape codes otherwise. For example, the null byte becomes `\0`.
|
98
|
+
|
99
|
+
[xml-charsets]: https://www.w3.org/TR/xml/#charsets
|
100
|
+
|
101
|
+
## Development
|
102
|
+
|
103
|
+
Run the specs with `bundle exec rake`, which uses [Appraisal][appraisal] to run the specs against all supported versions of rspec.
|
104
|
+
|
105
|
+
[appraisal]: https://github.com/thoughtbot/appraisal
|
106
|
+
|
107
|
+
## Releasing
|
108
|
+
|
109
|
+
Bump the gem version in the gemspec, and commit. Then `bundle exec rake build` to build a gem package, `bundle exec rake install` to install and test it locally, then `bundle exec rake release` to tag and push the commits and gem.
|
37
110
|
|
38
111
|
## License
|
39
112
|
|
40
|
-
The MIT License, see [LICENSE]
|
113
|
+
The MIT License, see [LICENSE](./LICENSE).
|
114
|
+
|
115
|
+
## Thanks
|
116
|
+
|
117
|
+
Inspired by the work of [Diego Souza][dgvncsz0f] on [RSpec Formatters][dgvncsz0f/rspec_formatters] after frustration with [CI Reporter][ci_reporter].
|
41
118
|
|
42
|
-
[rspec]: http://rspec.info/
|
43
|
-
[jenkins]: http://jenkins-ci.org/
|
44
119
|
[dgvncsz0f]: https://github.com/dgvncsz0f
|
45
120
|
[dgvncsz0f/rspec_formatters]: https://github.com/dgvncsz0f/rspec_formatters
|
46
121
|
[ci_reporter]: https://github.com/nicksieger/ci_reporter
|
47
|
-
[bundler]: http://gembundler.com/
|
48
|
-
[fuubar]: http://jeffkreeftmeijer.com/2010/fuubar-the-instafailing-rspec-progress-bar-formatter/
|
49
|
-
[license]: https://github.com/sj26/rspec-junit-formatter/blob/master/LICENSE
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
|
2
4
|
attr_reader :started
|
3
5
|
|
@@ -13,14 +15,8 @@ class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
|
|
13
15
|
|
14
16
|
private
|
15
17
|
|
16
|
-
def xml_dump_examples
|
17
|
-
examples.each do |example|
|
18
|
-
send :"xml_dump_#{example.execution_result[:status]}", example
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
18
|
def result_of(example)
|
23
|
-
example.execution_result[:status]
|
19
|
+
example.execution_result[:status].to_sym
|
24
20
|
end
|
25
21
|
|
26
22
|
def example_group_file_path_for(example)
|
@@ -48,7 +44,43 @@ private
|
|
48
44
|
example.execution_result[:exception]
|
49
45
|
end
|
50
46
|
|
51
|
-
def
|
52
|
-
|
47
|
+
def failure_type_for(example)
|
48
|
+
exception_for(example).class.name
|
49
|
+
end
|
50
|
+
|
51
|
+
def failure_message_for(example)
|
52
|
+
strip_diff_colors(exception_for(example).to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
def failure_for(example)
|
56
|
+
exception = exception_for(example)
|
57
|
+
message = strip_diff_colors(exception.message)
|
58
|
+
backtrace = format_backtrace(exception.backtrace, example)
|
59
|
+
|
60
|
+
if shared_group = find_shared_group(example)
|
61
|
+
backtrace << "Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\" called from #{shared_group.metadata[:example_group][:location]}"
|
62
|
+
end
|
63
|
+
|
64
|
+
"#{message}\n#{backtrace.join("\n")}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def error_count
|
68
|
+
0
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_shared_group(example)
|
72
|
+
group_and_parent_groups(example).find { |group| group.metadata[:shared_group_name] }
|
73
|
+
end
|
74
|
+
|
75
|
+
def group_and_parent_groups(example)
|
76
|
+
example.example_group.parent_groups + [example.example_group]
|
77
|
+
end
|
78
|
+
|
79
|
+
def stdout_for(example)
|
80
|
+
example.metadata[:stdout]
|
81
|
+
end
|
82
|
+
|
83
|
+
def stderr_for(example)
|
84
|
+
example.metadata[:stderr]
|
53
85
|
end
|
54
86
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
|
2
4
|
RSpec::Core::Formatters.register self,
|
3
5
|
:start,
|
@@ -16,7 +18,7 @@ class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
|
|
16
18
|
|
17
19
|
def dump_summary(notification)
|
18
20
|
@summary_notification = notification
|
19
|
-
xml_dump
|
21
|
+
without_color { xml_dump }
|
20
22
|
end
|
21
23
|
|
22
24
|
private
|
@@ -24,11 +26,15 @@ private
|
|
24
26
|
attr_reader :started
|
25
27
|
|
26
28
|
def example_count
|
27
|
-
@summary_notification.
|
29
|
+
@summary_notification.example_count
|
30
|
+
end
|
31
|
+
|
32
|
+
def pending_count
|
33
|
+
@summary_notification.pending_count
|
28
34
|
end
|
29
35
|
|
30
36
|
def failure_count
|
31
|
-
@summary_notification.
|
37
|
+
@summary_notification.failure_count
|
32
38
|
end
|
33
39
|
|
34
40
|
def duration
|
@@ -39,12 +45,25 @@ private
|
|
39
45
|
@examples_notification.notifications
|
40
46
|
end
|
41
47
|
|
48
|
+
def error_count
|
49
|
+
# Introduced in rspec 3.6
|
50
|
+
if @summary_notification.respond_to?(:errors_outside_of_examples_count)
|
51
|
+
@summary_notification.errors_outside_of_examples_count
|
52
|
+
else
|
53
|
+
0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
42
57
|
def result_of(notification)
|
43
58
|
notification.example.execution_result.status
|
44
59
|
end
|
45
60
|
|
46
61
|
def example_group_file_path_for(notification)
|
47
|
-
notification.example.example_group
|
62
|
+
metadata = notification.example.metadata[:example_group]
|
63
|
+
while parent_metadata = metadata[:parent_example_group]
|
64
|
+
metadata = parent_metadata
|
65
|
+
end
|
66
|
+
metadata[:file_path]
|
48
67
|
end
|
49
68
|
|
50
69
|
def classname_for(notification)
|
@@ -60,11 +79,80 @@ private
|
|
60
79
|
notification.example.full_description
|
61
80
|
end
|
62
81
|
|
82
|
+
def failure_type_for(example)
|
83
|
+
exception_for(example).class.name
|
84
|
+
end
|
85
|
+
|
86
|
+
def failure_message_for(example)
|
87
|
+
strip_diff_colors(exception_for(example).to_s)
|
88
|
+
end
|
89
|
+
|
90
|
+
def failure_for(notification)
|
91
|
+
strip_diff_colors(notification.message_lines.join("\n")) << "\n" << notification.formatted_backtrace.join("\n")
|
92
|
+
end
|
93
|
+
|
63
94
|
def exception_for(notification)
|
64
95
|
notification.example.execution_result.exception
|
65
96
|
end
|
66
97
|
|
67
|
-
|
68
|
-
|
98
|
+
# rspec makes it really difficult to swap in configuration temporarily due to
|
99
|
+
# the way it cascades defaults, command line arguments, and user
|
100
|
+
# configuration. This method makes sure configuration gets swapped in
|
101
|
+
# correctly, but also that the original state is definitely restored.
|
102
|
+
def swap_rspec_configuration(key, value)
|
103
|
+
unset = Object.new
|
104
|
+
force = RSpec.configuration.send(:value_for, key) { unset }
|
105
|
+
if unset.equal?(force)
|
106
|
+
previous = RSpec.configuration.send(key)
|
107
|
+
RSpec.configuration.send(:"#{key}=", value)
|
108
|
+
else
|
109
|
+
RSpec.configuration.force({key => value})
|
110
|
+
end
|
111
|
+
yield
|
112
|
+
ensure
|
113
|
+
if unset.equal?(force)
|
114
|
+
RSpec.configuration.send(:"#{key}=", previous)
|
115
|
+
else
|
116
|
+
RSpec.configuration.force({key => force})
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Completely gross hack for absolutely forcing off colorising for the
|
121
|
+
# duration of a block.
|
122
|
+
if RSpec.configuration.respond_to?(:color_mode=)
|
123
|
+
def without_color(&block)
|
124
|
+
swap_rspec_configuration(:color_mode, :off, &block)
|
125
|
+
end
|
126
|
+
elsif RSpec.configuration.respond_to?(:color=)
|
127
|
+
def without_color(&block)
|
128
|
+
swap_rspec_configuration(:color, false, &block)
|
129
|
+
end
|
130
|
+
else
|
131
|
+
warn 'rspec_junit_formatter cannot prevent colorising due to an unexpected RSpec.configuration format'
|
132
|
+
def without_color
|
133
|
+
yield
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def stdout_for(example_notification)
|
138
|
+
example_notification.example.metadata[:stdout]
|
139
|
+
end
|
140
|
+
|
141
|
+
def stderr_for(example_notification)
|
142
|
+
example_notification.example.metadata[:stderr]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# rspec-core 3.0.x forgot to mark this as a module function which causes:
|
147
|
+
#
|
148
|
+
# NoMethodError: undefined method `wrap' for RSpec::Core::Notifications::NullColorizer:Class
|
149
|
+
# .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:229:in `add_shared_group_line'
|
150
|
+
# .../rspec-core-3.0.4/lib/rspec/core/notifications.rb:157:in `message_lines'
|
151
|
+
#
|
152
|
+
if defined?(RSpec::Core::Notifications::NullColorizer) && RSpec::Core::Notifications::NullColorizer.is_a?(Class) && !RSpec::Core::Notifications::NullColorizer.respond_to?(:wrap)
|
153
|
+
RSpec::Core::Notifications::NullColorizer.class_eval do
|
154
|
+
def self.wrap(*args)
|
155
|
+
new.wrap(*args)
|
156
|
+
end
|
69
157
|
end
|
70
158
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
1
4
|
require "time"
|
2
|
-
require "builder"
|
3
5
|
|
4
6
|
require "rspec/core"
|
5
7
|
require "rspec/core/formatters/base_formatter"
|
@@ -11,55 +13,183 @@ class RSpecJUnitFormatter < RSpec::Core::Formatters::BaseFormatter
|
|
11
13
|
|
12
14
|
private
|
13
15
|
|
14
|
-
def xml
|
15
|
-
@xml ||= Builder::XmlMarkup.new target: output, indent: 2
|
16
|
-
end
|
17
|
-
|
18
16
|
def xml_dump
|
19
|
-
xml.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
output << %{<?xml version="1.0" encoding="UTF-8"?>\n}
|
18
|
+
output << %{<testsuite}
|
19
|
+
output << %{ name="rspec#{escape(ENV["TEST_ENV_NUMBER"].to_s)}"}
|
20
|
+
output << %{ tests="#{example_count}"}
|
21
|
+
output << %{ skipped="#{pending_count}"}
|
22
|
+
output << %{ failures="#{failure_count}"}
|
23
|
+
output << %{ errors="#{error_count}"}
|
24
|
+
output << %{ time="#{escape("%.6f" % duration)}"}
|
25
|
+
output << %{ timestamp="#{escape(started.iso8601)}"}
|
26
|
+
output << %{ hostname="#{escape(Socket.gethostname)}"}
|
27
|
+
output << %{>\n}
|
28
|
+
output << %{<properties>\n}
|
29
|
+
output << %{<property}
|
30
|
+
output << %{ name="seed"}
|
31
|
+
output << %{ value="#{escape(RSpec.configuration.seed.to_s)}"}
|
32
|
+
output << %{/>\n}
|
33
|
+
output << %{</properties>\n}
|
34
|
+
xml_dump_examples
|
35
|
+
output << %{</testsuite>\n}
|
25
36
|
end
|
26
37
|
|
27
38
|
def xml_dump_examples
|
28
39
|
examples.each do |example|
|
29
|
-
|
40
|
+
case result_of(example)
|
41
|
+
when :pending
|
42
|
+
xml_dump_pending(example)
|
43
|
+
when :failed
|
44
|
+
xml_dump_failed(example)
|
45
|
+
else
|
46
|
+
xml_dump_example(example)
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
|
33
|
-
def xml_dump_passed(example)
|
34
|
-
xml_dump_example(example)
|
35
|
-
end
|
36
|
-
|
37
51
|
def xml_dump_pending(example)
|
38
52
|
xml_dump_example(example) do
|
39
|
-
|
53
|
+
output << %{<skipped/>}
|
40
54
|
end
|
41
55
|
end
|
42
56
|
|
43
57
|
def xml_dump_failed(example)
|
44
|
-
exception = exception_for(example)
|
45
|
-
backtrace = formatted_backtrace_for(example)
|
46
|
-
|
47
58
|
xml_dump_example(example) do
|
48
|
-
|
49
|
-
|
50
|
-
|
59
|
+
output << %{<failure}
|
60
|
+
output << %{ message="#{escape(failure_message_for(example))}"}
|
61
|
+
output << %{ type="#{escape(failure_type_for(example))}"}
|
62
|
+
output << %{>}
|
63
|
+
output << escape(failure_for(example))
|
64
|
+
output << %{</failure>}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def xml_dump_example(example)
|
69
|
+
output << %{<testcase}
|
70
|
+
output << %{ classname="#{escape(classname_for(example))}"}
|
71
|
+
output << %{ name="#{escape(description_for(example))}"}
|
72
|
+
output << %{ file="#{escape(example_group_file_path_for(example))}"}
|
73
|
+
if duration = duration_for(example)
|
74
|
+
output << %{ time="#{escape("%.6f" % duration)}"}
|
51
75
|
end
|
76
|
+
output << %{>}
|
77
|
+
yield if block_given?
|
78
|
+
xml_dump_output(example)
|
79
|
+
output << %{</testcase>\n}
|
52
80
|
end
|
53
81
|
|
54
|
-
def
|
55
|
-
|
82
|
+
def xml_dump_output(example)
|
83
|
+
if (stdout = stdout_for(example)) && !stdout.empty?
|
84
|
+
output << %{<system-out>}
|
85
|
+
output << escape(stdout)
|
86
|
+
output << %{</system-out>}
|
87
|
+
end
|
88
|
+
|
89
|
+
if (stderr = stderr_for(example)) && !stderr.empty?
|
90
|
+
output << %{<system-err>}
|
91
|
+
output << escape(stderr)
|
92
|
+
output << %{</system-err>}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Inversion of character range from https://www.w3.org/TR/xml/#charsets
|
97
|
+
ILLEGAL_REGEXP = Regexp.new(
|
98
|
+
"[^".dup <<
|
99
|
+
"\u{9}" << # => \t
|
100
|
+
"\u{a}" << # => \n
|
101
|
+
"\u{d}" << # => \r
|
102
|
+
"\u{20}-\u{d7ff}" <<
|
103
|
+
"\u{e000}-\u{fffd}" <<
|
104
|
+
"\u{10000}-\u{10ffff}" <<
|
105
|
+
"]"
|
106
|
+
)
|
107
|
+
|
108
|
+
# Replace illegals with a Ruby-like escape
|
109
|
+
ILLEGAL_REPLACEMENT = Hash.new { |_, c|
|
110
|
+
x = c.ord
|
111
|
+
if x <= 0xff
|
112
|
+
"\\x%02X".freeze % x
|
113
|
+
elsif x <= 0xffff
|
114
|
+
"\\u%04X".freeze % x
|
115
|
+
else
|
116
|
+
"\\u{%X}".freeze % x
|
117
|
+
end.freeze
|
118
|
+
}.update(
|
119
|
+
"\0".freeze => "\\0".freeze,
|
120
|
+
"\a".freeze => "\\a".freeze,
|
121
|
+
"\b".freeze => "\\b".freeze,
|
122
|
+
"\f".freeze => "\\f".freeze,
|
123
|
+
"\v".freeze => "\\v".freeze,
|
124
|
+
"\e".freeze => "\\e".freeze,
|
125
|
+
).freeze
|
126
|
+
|
127
|
+
# Discouraged characters from https://www.w3.org/TR/xml/#charsets
|
128
|
+
# Plus special characters with well-known entity replacements
|
129
|
+
DISCOURAGED_REGEXP = Regexp.new(
|
130
|
+
"[".dup <<
|
131
|
+
"\u{22}" << # => "
|
132
|
+
"\u{26}" << # => &
|
133
|
+
"\u{27}" << # => '
|
134
|
+
"\u{3c}" << # => <
|
135
|
+
"\u{3e}" << # => >
|
136
|
+
"\u{7f}-\u{84}" <<
|
137
|
+
"\u{86}-\u{9f}" <<
|
138
|
+
"\u{fdd0}-\u{fdef}" <<
|
139
|
+
"\u{1fffe}-\u{1ffff}" <<
|
140
|
+
"\u{2fffe}-\u{2ffff}" <<
|
141
|
+
"\u{3fffe}-\u{3ffff}" <<
|
142
|
+
"\u{4fffe}-\u{4ffff}" <<
|
143
|
+
"\u{5fffe}-\u{5ffff}" <<
|
144
|
+
"\u{6fffe}-\u{6ffff}" <<
|
145
|
+
"\u{7fffe}-\u{7ffff}" <<
|
146
|
+
"\u{8fffe}-\u{8ffff}" <<
|
147
|
+
"\u{9fffe}-\u{9ffff}" <<
|
148
|
+
"\u{afffe}-\u{affff}" <<
|
149
|
+
"\u{bfffe}-\u{bffff}" <<
|
150
|
+
"\u{cfffe}-\u{cffff}" <<
|
151
|
+
"\u{dfffe}-\u{dffff}" <<
|
152
|
+
"\u{efffe}-\u{effff}" <<
|
153
|
+
"\u{ffffe}-\u{fffff}" <<
|
154
|
+
"\u{10fffe}-\u{10ffff}" <<
|
155
|
+
"]"
|
156
|
+
)
|
157
|
+
|
158
|
+
# Translate well-known entities, or use generic unicode hex entity
|
159
|
+
DISCOURAGED_REPLACEMENTS = Hash.new { |_, c| "&#x#{c.ord.to_s(16)};".freeze }.update(
|
160
|
+
?".freeze => """.freeze,
|
161
|
+
?&.freeze => "&".freeze,
|
162
|
+
?'.freeze => "'".freeze,
|
163
|
+
?<.freeze => "<".freeze,
|
164
|
+
?>.freeze => ">".freeze,
|
165
|
+
).freeze
|
166
|
+
|
167
|
+
def escape(text)
|
168
|
+
# Make sure it's utf-8, replace illegal characters with ruby-like escapes, and replace special and discouraged characters with entities
|
169
|
+
text.to_s.encode(Encoding::UTF_8).gsub(ILLEGAL_REGEXP, ILLEGAL_REPLACEMENT).gsub(DISCOURAGED_REGEXP, DISCOURAGED_REPLACEMENTS)
|
170
|
+
end
|
171
|
+
|
172
|
+
STRIP_DIFF_COLORS_BLOCK_REGEXP = /^ ( [ ]* ) Diff: (?: \e\[ 0 m )? (?: \n \1 \e\[ \d+ (?: ; \d+ )* m .* )* /x
|
173
|
+
STRIP_DIFF_COLORS_CODES_REGEXP = /\e\[ \d+ (?: ; \d+ )* m/x
|
174
|
+
|
175
|
+
def strip_diff_colors(string)
|
176
|
+
# XXX: RSpec diffs are appended to the message lines fairly early and will
|
177
|
+
# contain ANSI escape codes for colorizing terminal output if the global
|
178
|
+
# rspec configuration is turned on, regardless of which notification lines
|
179
|
+
# we ask for. We need to strip the codes from the diff part of the message
|
180
|
+
# for XML output here.
|
181
|
+
#
|
182
|
+
# We also only want to target the diff hunks because the failure message
|
183
|
+
# itself might legitimately contain ansi escape codes.
|
184
|
+
#
|
185
|
+
string.sub(STRIP_DIFF_COLORS_BLOCK_REGEXP) { |match| match.gsub(STRIP_DIFF_COLORS_CODES_REGEXP, "".freeze) }
|
56
186
|
end
|
57
187
|
end
|
58
188
|
|
59
189
|
RspecJunitFormatter = RSpecJUnitFormatter
|
60
190
|
|
61
|
-
if RSpec::Core::Version::STRING.
|
191
|
+
if Gem::Version.new(RSpec::Core::Version::STRING) >= Gem::Version.new("3")
|
62
192
|
require "rspec_junit_formatter/rspec3"
|
63
|
-
else
|
193
|
+
else
|
64
194
|
require "rspec_junit_formatter/rspec2"
|
65
195
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,34 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec_junit_formatter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Cochran
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain:
|
11
11
|
- |
|
12
12
|
-----BEGIN CERTIFICATE-----
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MIIDXDCCAkSgAwIBAgIBATANBgkqhkiG9w0BAQsFADA6MQ0wCwYDVQQDDARzajI2
|
14
|
+
MRQwEgYKCZImiZPyLGQBGRYEc2oyNjETMBEGCgmSJomT8ixkARkWA2NvbTAeFw0y
|
15
|
+
MjA3MDQwMDQwNDZaFw0yMzA3MDQwMDQwNDZaMDoxDTALBgNVBAMMBHNqMjYxFDAS
|
16
16
|
BgoJkiaJk/IsZAEZFgRzajI2MRMwEQYKCZImiZPyLGQBGRYDY29tMIIBIjANBgkq
|
17
17
|
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr60Eo/ttCk8GMTMFiPr3GoYMIMFvLak
|
18
18
|
xSmTk9YGCB6UiEePB4THSSA5w6IPyeaCF/nWkDp3/BAam0eZMWG1IzYQB23TqIM0
|
19
19
|
1xzcNRvFsn0aQoQ00k+sj+G83j3T5OOV5OZIlu8xAChMkQmiPd1NXc6uFv+Iacz7
|
20
20
|
kj+CMsI9YUFdNoU09QY0b+u+Rb6wDYdpyvN60YC30h0h1MeYbvYZJx/iZK4XY5zu
|
21
21
|
4O/FL2ChjL2CPCpLZW55ShYyrzphWJwLOJe+FJ/ZBl6YXwrzQM9HKnt4titSNvyU
|
22
|
-
KzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
KzE3L63A3PZvExzLrN9u09kuWLLJfXB2sGOlw3n9t72rJiuBr3/OQQIDAQABo20w
|
23
|
+
azAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU99dfRjEKFyczTeIz
|
24
|
+
m3ZsDWrNC80wGAYDVR0RBBEwD4ENc2oyNkBzajI2LmNvbTAYBgNVHRIEETAPgQ1z
|
25
|
+
ajI2QHNqMjYuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCsa7k3TABBcyXotr3yCq6f
|
26
|
+
xsgbMG9FR71c4wRgVNQi9O3jN64fQBbxo//BQlHfPCjs1CeU4es9xdQFfhqXAPXG
|
27
|
+
P7mK3+qd5jObjh6l3/rDKrTXNS+P+YO/1frlZ6xPjCA8XgGc4y0rhAjZnVBDV6t1
|
28
|
+
kmdtEmue1s1OxaMakr78XRZDxEuAeLM5fg8MYnlOFygEcAH6lZkTjXavY7s9MXRB
|
29
|
+
AAMioxgB6J5QhXQ42OSWIzwHZIbSv3DV9Lf5sde50HIW5f9u5jn29TUGDhSWYKkh
|
30
|
+
LDvy9dfwMMOdIZi75Q8SBBib84AuwhMHIlUv9FcHhh3dXsDDYkrVrpUAwCsG6yCm
|
30
31
|
-----END CERTIFICATE-----
|
31
|
-
date:
|
32
|
+
date: 2022-09-29 00:00:00.000000000 Z
|
32
33
|
dependencies:
|
33
34
|
- !ruby/object:Gem::Dependency
|
34
35
|
name: rspec-core
|
@@ -57,34 +58,82 @@ dependencies:
|
|
57
58
|
- !ruby/object:Gem::Version
|
58
59
|
version: 2.12.0
|
59
60
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
61
|
+
name: bundler
|
61
62
|
requirement: !ruby/object:Gem::Requirement
|
62
63
|
requirements:
|
63
|
-
- - "
|
64
|
+
- - ">="
|
64
65
|
- !ruby/object:Gem::Version
|
65
|
-
version: '
|
66
|
-
type: :
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
67
68
|
prerelease: false
|
68
69
|
version_requirements: !ruby/object:Gem::Requirement
|
69
70
|
requirements:
|
70
|
-
- - "
|
71
|
+
- - ">="
|
71
72
|
- !ruby/object:Gem::Version
|
72
|
-
version: '
|
73
|
+
version: '0'
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: appraisal
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
type: :development
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
73
88
|
- !ruby/object:Gem::Dependency
|
74
89
|
name: nokogiri
|
75
90
|
requirement: !ruby/object:Gem::Requirement
|
76
91
|
requirements:
|
77
92
|
- - "~>"
|
78
93
|
- !ruby/object:Gem::Version
|
79
|
-
version: '1.
|
94
|
+
version: '1.8'
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.8.2
|
80
98
|
type: :development
|
81
99
|
prerelease: false
|
82
100
|
version_requirements: !ruby/object:Gem::Requirement
|
83
101
|
requirements:
|
84
102
|
- - "~>"
|
85
103
|
- !ruby/object:Gem::Version
|
86
|
-
version: '1.
|
87
|
-
|
104
|
+
version: '1.8'
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 1.8.2
|
108
|
+
- !ruby/object:Gem::Dependency
|
109
|
+
name: rake
|
110
|
+
requirement: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
type: :development
|
116
|
+
prerelease: false
|
117
|
+
version_requirements: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
- !ruby/object:Gem::Dependency
|
123
|
+
name: coderay
|
124
|
+
requirement: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
type: :development
|
130
|
+
prerelease: false
|
131
|
+
version_requirements: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
description: RSpec results that your continuous integration service can read.
|
88
137
|
email: sj26@sj26.com
|
89
138
|
executables: []
|
90
139
|
extensions: []
|
@@ -95,11 +144,12 @@ files:
|
|
95
144
|
- lib/rspec_junit_formatter.rb
|
96
145
|
- lib/rspec_junit_formatter/rspec2.rb
|
97
146
|
- lib/rspec_junit_formatter/rspec3.rb
|
98
|
-
homepage:
|
147
|
+
homepage: https://github.com/sj26/rspec_junit_formatter
|
99
148
|
licenses:
|
100
149
|
- MIT
|
101
|
-
metadata:
|
102
|
-
|
150
|
+
metadata:
|
151
|
+
changelog_uri: https://github.com/sj26/rspec_junit_formatter/blob/HEAD/CHANGELOG.md
|
152
|
+
post_install_message:
|
103
153
|
rdoc_options: []
|
104
154
|
require_paths:
|
105
155
|
- lib
|
@@ -107,16 +157,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
157
|
requirements:
|
108
158
|
- - ">="
|
109
159
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
160
|
+
version: 2.3.0
|
111
161
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
162
|
requirements:
|
113
163
|
- - ">="
|
114
164
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
165
|
+
version: 2.0.0
|
116
166
|
requirements: []
|
117
|
-
|
118
|
-
|
119
|
-
signing_key:
|
167
|
+
rubygems_version: 3.3.7
|
168
|
+
signing_key:
|
120
169
|
specification_version: 4
|
121
170
|
summary: RSpec JUnit XML formatter
|
122
171
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|