xcpretty 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b78a4012d3b1df36cbd26eee764159f4a2ee5e14
4
- data.tar.gz: 8744a4e46db1e1983611a445b733208c4d12f177
3
+ metadata.gz: 46fc2cf7d4b723b36c5944ac2dae1e935f40987a
4
+ data.tar.gz: e703e40ee1a621283885b2efae96b6e8406f4f84
5
5
  SHA512:
6
- metadata.gz: 9e7ff5c8ce55088cd935aa120aec5bbe154f876201211d193345349372a5e3100a0f38c2d55c460e2c380ddebfee1f9f9d1e0eee1f0fc930d95a512e27fe1339
7
- data.tar.gz: b54c6a0c5d506252a6ae6eb07ee0ddbb40d468757f104decf4219b357527f40eb33f8ac8664a3b221de88d083c613b30f0c4534925489e85452f8443cc1d5daf
6
+ metadata.gz: a8d7effa24f7900014019d49d58c9bfc4b2bb4e9e0eec3db61e1432a4ac5f3a92a036c15c81e2a9092a549d6b47ff6c6cee57d97f22704392ee74c687230adf1
7
+ data.tar.gz: b39b856bbcc21faf8249df6014c6141861beb327d8236181169bc31ecebccb6eba02f8f4357652ba74f7a8ff28f1d4120a4d1a24e0b883c6443722ffa75bb98c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ ###### Enhancements
6
+
7
+ * Indented test runs by suite
8
+ * Added HTML reporter
9
+
10
+ ###### Misc
11
+
12
+ * removed the faux exit statuts hangling. use `exit ${PIPESTATUS[0]}`
13
+
14
+
3
15
  ## 0.1.2
4
16
 
5
17
  ###### Enhancements
data/README.md CHANGED
@@ -1,27 +1,28 @@
1
- # XCPretty
1
+ # xcpretty
2
2
 
3
- __XCPretty is a fast and flexible formatter for `xcodebuild`__.<br/>
3
+ __`xcpretty` is a fast and flexible formatter for `xcodebuild`__.<br/>
4
4
  It does one thing, and it should do it well.
5
5
 
6
- [![Build Status](https://travis-ci.org/mneorr/XCPretty.png?branch=master)](https://travis-ci.org/mneorr/XCPretty)
6
+ [![Build Status](https://travis-ci.org/mneorr/xcpretty.png?branch=master)](https://travis-ci.org/mneorr/xcpretty)
7
7
  [![Code Climate](https://codeclimate.com/github/mneorr/XCPretty.png)](https://codeclimate.com/github/mneorr/XCPretty)
8
- ## Installation
9
-
10
- $ gem install xcpretty
11
8
 
12
- XCPretty requires Ruby 1.8.7 or above.
9
+ ## Installation
10
+ ``` bash
11
+ $ gem install xcpretty
12
+ ```
13
13
 
14
14
  ## Usage
15
-
16
- XCPretty is designed to be piped with `xcodebuild` and thus keeping 100% compatibility with it.
17
- This means, when `xcodebuild` works, `xcpretty` works.
15
+ ``` bash
16
+ $ xcodebuild [flags] | xcpretty -c
17
+ ```
18
+ `xcpretty` is designed to be piped with `xcodebuild` and thus keeping 100% compatibility with it.
18
19
  It's even a bit faster than `xcodebuild` only, since it saves your terminal some prints.
19
20
 
20
- __Important:__ lots of CIs are using exit status for determining if build has
21
- failed. You probably want to exit with the same status code as `xcodebuild`.
21
+ __Important:__ If you're running `xcpretty` on a CI like Travis or Jenkins, you may want to exit with same status code as `xcodebuild`.
22
+ CI uses the status code to determine if build has failed.
22
23
 
23
- ```
24
- xcodebuild ... | xcpretty -c; exit ${PIPESTATUS[0]}
24
+ ``` bash
25
+ $ xcodebuild [flags] | xcpretty -c; exit ${PIPESTATUS[0]}
25
26
  ```
26
27
 
27
28
  ## Formats
@@ -33,37 +34,31 @@ xcodebuild ... | xcpretty -c; exit ${PIPESTATUS[0]}
33
34
  - `--test`, `-t` (RSpec style)
34
35
  ![xcpretty alpha](http://i.imgur.com/VeTQQub.gif)
35
36
 
36
- - tun / tap (not yet implemented. possible solution for most CI servers)
37
37
 
38
38
  ## Reporters
39
39
 
40
40
  - `--report junit`, `-r junit`: Creates a JUnit-style XML report at `build/reports/junit.xml`, compatible with Jenkins CI.
41
41
 
42
- ## Have you just cloned xctool?
42
+ - `--report html`, `-r html`: Creates a simple HTML report at `build/reports/tests.html`.
43
+ ![xcpretty html](http://i.imgur.com/0Rnux3v.gif)
44
+
45
+ Writing a report to a custom path can be specified using `--output PATH`.
46
+
47
+ ## Did you just clone xctool?
43
48
 
44
49
  Unlike [xctool](https://github.com/facebook/xctool), `xcpretty` isn't a build tool.
45
50
  It relies on `xcodebuild` to do the build process, and it formats the output.
46
51
 
47
52
  By the time when [xctool](https://github.com/facebook/xctool) was made, `xcodebuild`
48
- wasn't aware of the `test` command. That means, running tests in general via CLI was a pain.
49
- At this point, `xcodebuild` has got improved a lot, and it's ready to be used directly.
50
-
51
- ## Why?
52
-
53
- There are many usages of this tool. Let me give you some ideas:
54
- - Xcode's test tools are close to useless. Failures in a sidebar, non-dettachable console,... You can use `xcpretty` to build your next Xcode test runner plugin
55
- - Run tests each time you hit save. Use [xclisten](https://github.com/mneorr/xclisten) for that
56
- - Mine Bitcoins. You can't with this tool, but you'll be so productive that you can earn all the money and buy them!!!1!
53
+ wasn't aware of the `test` command, thus running tests in general via CLI was a pain.
54
+ At this point `xcodebuild` has been improved significantly, and is ready to be used directly.
57
55
 
58
- ## Roadmap
59
- - Improve test reporting, group tests semantically
60
- - Write original xcodebuild output with -o flag
61
56
 
62
57
  ## Benchmark
63
58
 
64
59
  A smaller project ([ObjectiveSugar](https://github.com/mneorr/objectivesugar)) with a fast suite
65
60
 
66
- #### XCPretty
61
+ #### xcpretty
67
62
  ```
68
63
  $ time xcodebuild -workspace ObjectiveSugar.xcworkspace -scheme ObjectiveSugar -sdk iphonesimulator test | xcpretty -tc
69
64
  ....................................................................................
@@ -80,7 +75,7 @@ Executed 84 tests, with 0 failures (0 unexpected) in 0.103 (0.129) seconds
80
75
 
81
76
  4.35 real 6.07 user 2.21 sys
82
77
  ```
83
- #### XCtool
78
+ #### xctool
84
79
  ```
85
80
  $ time xctool -workspace ObjectiveSugar.xcworkspace -scheme ObjectiveSugar -sdk iphonesimulator test
86
81
  ... ommitted output ...
@@ -91,7 +86,7 @@ $ time xctool -workspace ObjectiveSugar.xcworkspace -scheme ObjectiveSugar -sdk
91
86
 
92
87
  A bit bigger project, without CocoaPods ([ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa))
93
88
 
94
- #### XCPretty
89
+ #### xcpretty
95
90
  ```
96
91
  $ time xcodebuild -project ReactiveCocoa.xcodeproj -scheme ReactiveCocoa test | xcpretty -tc
97
92
  ..........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
@@ -108,7 +103,7 @@ Executed 922 tests, with 0 failures (0 unexpected) in 6.542 (6.913) seconds
108
103
 
109
104
  8.82 real 5.65 user 0.75 sys
110
105
  ```
111
- #### XCtool
106
+ #### xctool
112
107
  ```
113
108
  $ time xctool -project ReactiveCocoa.xcodeproj -scheme ReactiveCocoa test
114
109
  ... ommitted output ...
@@ -0,0 +1,155 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Test Results | xcpretty</title>
6
+ <style type="text/css">
7
+ body { font-family:Avenir Next, Helvetica Neue, sans-serif; color: #4A4A4A; background-color: #F0F3FB; margin:0;}
8
+ h1 { font-weight: normal; font-size: 24px; margin: 10px 0 0 0;}
9
+ h3 { font-weight: normal; margin: 2px; font-size: 1.1em;}
10
+ header { position: fixed;width: 100%;background: rgba(249, 254, 255, 0.9);margin: 0;padding: 10px;}
11
+ header:before, header:after { content:""; display:table;}
12
+ header:after { clear:both;}
13
+ a:link { color: #A1D761;}
14
+ footer { clear: both;position: relative;z-index: 10;height: 40px;margin-top: -10px; margin-left:30px; font-size:12px;}
15
+ table { width:100%; border-collapse: collapse;}
16
+ tr td:first-child { width:7%}
17
+ .left { float: left; margin-left:30px;}
18
+ .right { float: right; margin-right: 40px; margin-top: 0; margin-bottom:0;}
19
+ .test-suite { margin: 0 0 30px 0;}
20
+ .test-suite > .heading { font-family:Menlo, Monaco, monospace; font-weight: bold; border-color: #A1D761; background-color: #B8E986; border-width: 1px;}
21
+ .test-suite.failing > .heading { border-color: #C84F5E; background-color: #E58591;}
22
+ .test-suite > .heading > .title { margin-top: 4px; margin-left: 10px;}
23
+ .tests { overflow: scroll;margin: 0 30px 0 60px;}
24
+ .test, .test-suite > .heading { height: 30px; overflow: hidden; margin: 0 30px;}
25
+ .test, .test-suite > .heading { border-width: 1px; border-collapse: collapse; border-style: solid; }
26
+ .test { margin-left: 30px; border-top:none;}
27
+ .test.failing { border-color: #C84F5E; background-color: #F4DDE0;}
28
+ .test.passing { border-color: #A1D761;}
29
+ .test.failing { background-color: #E7A1AA;}
30
+ .test.passing { background-color: #CAF59F;}
31
+ .test.failing.odd { background-color: #EEC7CC;}
32
+ .test.passing.odd { background-color: #E5FBCF;}
33
+ .details { background-color: #F4DDE0; border: 1px solid #C84F5E;}
34
+ .test .test-detail:last-child { padding-bottom: 8px;}
35
+ .test .title { float: left; font-size: 0.9em; margin-top: 8px; font-family: Menlo, Monaco, monospace;}
36
+ .test .time { float: left;margin: 4px 10px 0 20px;}
37
+ .test-detail { font-family:Menlo, Monaco, monospace; font-size: 0.9em; margin: 5px 0 5px 0px;}
38
+ #test-suites { display: inline-block; width: 100%;margin-top:100px;}
39
+ #segment-bar { margin-top: 10px;margin-left: 14px;float:right;}
40
+ #segment-bar a:first-child { border-radius: 9px 0 0 9px; border-right: none;}
41
+ #segment-bar a:last-child { border-radius: 0 9px 9px 0; border-left: none;}
42
+ #segment-bar > a { color: #565656; border: 2px solid #7B7B7B; width: 80px; font-weight: bold; display:inline-block;text-align:center; font-weight: normal;}
43
+ #segment-bar > a.selected { background-color: #979797; color: #F0F3FB;}
44
+ #counters { float: left;margin: 10px;text-align: right;}
45
+ #counters h2 { font-size: 16px; font-family: Avenir, sans-serif; font-weight: lighter; display:inline;}
46
+ #counters .number { font-size: 20px;}
47
+ #fail-count { color: #D0021B; margin-left:10px;}
48
+ @media (max-width: 640px) {
49
+ h1, #counters, #segment-bar { margin: 5px auto; text-align:center;}
50
+ header, #segment-bar { width: 100%; position: relative; background:none;}
51
+ .left, .right { float:none; margin:0;}
52
+ #test-suites { margin-top: 0;}
53
+ #counters { float:none;}
54
+ }
55
+ </style>
56
+ <script type="text/javascript">
57
+ var hide = function(element) { element.style.display = 'none';}
58
+ var show = function(element) { element.style.display = '';}
59
+ var isHidden = function(element) { return element.style.display == 'none';}
60
+ var isSelected = function(element) { return element.classList.contains("selected");}
61
+ var deselect = function(element) { return element.classList.remove("selected");}
62
+ var select = function(element) { return element.classList.add("selected");}
63
+ var toggle = function(element) { isHidden(element) ? show(element) : hide(element);};
64
+ var toggleTests = function(heading) { toggle(heading.parentNode.children[1]);};
65
+ var toggleDetails = function(detailClass) {
66
+ var details = document.querySelectorAll('.' + detailClass);
67
+ for (var i = details.length - 1; i >= 0; i--) { toggle(details[i]);};
68
+ };
69
+ var hideAll = function(collection) {
70
+ for (var i = collection.length - 1; i >= 0; i--) { hide(collection[i]); };
71
+ }
72
+ var showAll = function(collection) {
73
+ for (var i = collection.length - 1; i >= 0; i--) { show(collection[i]); };
74
+ }
75
+ var selectSegment = function(segment) {
76
+ if (isSelected(segment)) return;
77
+ var segments = document.querySelectorAll('#segment-bar > a');
78
+ for (var i = segments.length - 1; i >= 0; i--) { deselect(segments[i]);};
79
+ select(segment);
80
+ if (segment.id == "all-segment") {
81
+ showAll(document.querySelectorAll('.test-suite'));
82
+ showAll(document.querySelectorAll('.test'));
83
+ } else if (segment.id == "failing-segment") {
84
+ hideAll(document.querySelectorAll('.test.passing'));
85
+ showAll(document.querySelectorAll('.test.failing'));
86
+ hideAll(document.querySelectorAll('.test-suite.passing'));
87
+ showAll(document.querySelectorAll('.test-suite.failing'));
88
+ } else if (segment.id == "passing-segment") {
89
+ hideAll(document.querySelectorAll('.test.failing'));
90
+ showAll(document.querySelectorAll('.test.passing'));
91
+ hideAll(document.querySelectorAll('.test-suite.failing'));
92
+ showAll(document.querySelectorAll('.test-suite.passing'));
93
+ }
94
+ }
95
+ </script>
96
+ </head>
97
+ <body>
98
+ <header>
99
+ <section class="left">
100
+ <h1>Test Results</h1>
101
+ </section>
102
+ <section class="right">
103
+ <section id="counters">
104
+ <h2 id="test-count"><span class="number"><%= test_count %></span> tests</h2>
105
+ <% if fail_count > 0 %>
106
+ <h2 id="fail-count"><span class="number"><%= fail_count %></span> failures</h2>
107
+ <% end %>
108
+ </section>
109
+ <section id="segment-bar">
110
+ <a id="all-segment" onclick="selectSegment(this);" class="selected">All</a><a id="failing-segment" onclick="selectSegment(this);">Failing</a><a id="passing-segment" onclick="selectSegment(this);">Passing</a>
111
+ </section>
112
+ </section>
113
+ </header>
114
+ <section id="test-suites">
115
+ <% test_suites.each do |name, info| %>
116
+ <% next unless info[:tests].size > 0 %>
117
+ <section class="test-suite <%= info[:failing] ? 'failing' : 'passing'%>" id="<%= name %>">
118
+ <section class="heading" onclick="toggleTests(this);">
119
+ <h3 class="title"><%= name %></h3>
120
+ </section>
121
+ <section class="tests">
122
+ <table>
123
+ <% info[:tests].each_with_index do |test, index| %>
124
+ <% detail_class = test[:name].gsub(/\s/,'') %>
125
+ <tr class="test <%= test[:failing] ? 'failing' : 'passing'%> <%= index % 2 != 0 ? 'odd' :''%>" onclick="toggleDetails('<%= detail_class %>');">
126
+ <td>
127
+ <% if test[:time] %>
128
+ <h3 class="time"><%= test[:time] %>s</h3>
129
+ <% end %>
130
+ </td>
131
+ <td><h3 class="title"><%= test[:name] %></h3></td>
132
+ </tr>
133
+ <% if test[:reason] || test[:snippet] %>
134
+ <tr class="details <%= detail_class %>">
135
+ <td></td>
136
+ <td>
137
+ <% if test[:reason] %>
138
+ <section class="test-detail reason"><%= test[:reason] %></section>
139
+ <% end %>
140
+ <% if test[:snippet] %>
141
+ <section class="test-detail snippet"><%= test[:snippet] %></section>
142
+ <section class="test-detail"><%= test[:file] %></section>
143
+ <% end %>
144
+ </td>
145
+ </tr>
146
+ <% end %>
147
+ <% end %>
148
+ </table>
149
+ </section>
150
+ </section>
151
+ <% end %>
152
+ </section>
153
+ <footer>Report generated with <a href="https://github.com/mneorr/xcpretty">xcpretty</a></footer>
154
+ </body>
155
+ </html>
data/bin/xcpretty CHANGED
@@ -13,7 +13,8 @@ require 'optparse'
13
13
  report_options = []
14
14
  report_classes = []
15
15
  report_formats = {
16
- "junit" => XCPretty::JUnit
16
+ "junit" => XCPretty::JUnit,
17
+ "html" => XCPretty::HTML
17
18
  }
18
19
 
19
20
  printer_opts = {
@@ -46,7 +47,7 @@ OptionParser.new do |opts|
46
47
  end
47
48
  opts.on('-o', '--output PATH', 'Write report output to PATH') do |path|
48
49
  unless opts = report_options.last
49
- XCPretty.exit_with_error('Expected report format to be speficied before before output path')
50
+ XCPretty.exit_with_error('Expected report format to be specified before before output path')
50
51
  end
51
52
  opts[:path] = path
52
53
  end
@@ -63,11 +64,9 @@ printer = XCPretty::Printer.new(printer_opts)
63
64
  reporters = report_classes.compact.each_with_index.map {|k,i| k.new(report_options[i])}
64
65
 
65
66
  ARGF.each_line do |line|
66
- XCPretty::ExitStatus.handle(line)
67
67
  printer.pretty_print(line)
68
68
  reporters.each { |r| r.handle(line) }
69
69
  end
70
70
 
71
71
  reporters.each(&:finish)
72
- exit(XCPretty::ExitStatus.code)
73
72
 
@@ -0,0 +1,40 @@
1
+ Feature: Creating a HTML test report
2
+
3
+ Background:
4
+ Given the tests have started running
5
+
6
+ Scenario: Showing a test suite
7
+ Given I have a passing test in my suite
8
+ When I pipe to xcpretty with "--report html"
9
+ Then I should see a test suite section in HTML
10
+
11
+ Scenario: Showing failed tests
12
+ Given I have a failing test in my suite
13
+ When I pipe to xcpretty with "--report html"
14
+ Then I should see a failed test in HTML
15
+ And the failure counter should show 1 test
16
+
17
+ Scenario: Showing passing tests
18
+ Given I have a passing test in my suite
19
+ When I pipe to xcpretty with "--report html"
20
+ Then I should see a passing test in HTML
21
+
22
+ Scenario: Counting tests
23
+ Given I have a passing test in my suite
24
+ And I have a failing test in my suite
25
+ And the test suite has finished
26
+ When I pipe to xcpretty with "--report html"
27
+ Then I should see 2 tests in HTML
28
+
29
+ Scenario: Having many test classes
30
+ Given I have tests in my suite from 2 classes
31
+ When I pipe to xcpretty with "--report html"
32
+ Then I should see 2 test suite sections in HTML
33
+
34
+ Scenario: Writing to a custom file path
35
+ When I pipe to xcpretty with "--report html" and specify a custom path
36
+ Then I should have a test report in a custom path
37
+
38
+ Scenario: Writing to multiple custom file paths
39
+ When I pipe to xcpretty with two custom "html" report paths
40
+ Then I should have test reports in two custom paths
@@ -35,5 +35,5 @@ Feature: Creating a JUnit test report
35
35
  Then I should have a test report in a custom path
36
36
 
37
37
  Scenario: Writing to multiple custom file paths
38
- When I pipe to xcpretty with two custom report paths
38
+ When I pipe to xcpretty with two custom "junit" report paths
39
39
  Then I should have test reports in two custom paths
@@ -109,10 +109,6 @@ When(/^I pipe to xcpretty with a custom formatter$/) do
109
109
  run_xcpretty("-f #{formatter_path}")
110
110
  end
111
111
 
112
- When(/^I pipe to xcpretty$/) do
113
- run_xcpretty("")
114
- end
115
-
116
112
  Then(/^I should see a custom compilation message$/) do
117
113
  run_output.should start_with("😎 Compilation party time")
118
114
  end
@@ -211,7 +207,7 @@ Then(/^I should see a green passing test mark$/) do
211
207
  end
212
208
 
213
209
  Then(/^I should see a non-utf prefixed output$/) do
214
- run_output.should start_with(green("."))
210
+ run_output.should start_with(" " + green("."))
215
211
  end
216
212
 
217
213
  Then(/^I should not see the name of the test group$/) do
@@ -0,0 +1,23 @@
1
+ Then(/^I should see a test suite section in HTML$/) do
2
+ html_test_suites.first.should_not be_nil
3
+ end
4
+
5
+ Then(/^I should see a failed test in HTML$/) do
6
+ html_report_body.get_elements("//*[contains(@class, 'test failing')]/").to_a.size.should_not == 0
7
+ end
8
+
9
+ Then(/^the failure counter should show (\d+) tests?$/) do |fail_count|
10
+ html_report_body.get_elements("//*[@id='fail-count']/").first.elements.to_a.first.text.to_i.should == fail_count.to_i
11
+ end
12
+
13
+ Then(/^I should see a passing test in HTML$/) do
14
+ html_report_body.get_elements("//*[contains(@class, 'test passing')]/").to_a.size.should_not == 0
15
+ end
16
+
17
+ Then(/^I should see (\d+) tests in HTML$/) do |test_count|
18
+ html_report_body.get_elements("//*[contains(@class, 'test ')]/").size.should == test_count.to_i
19
+ end
20
+
21
+ Then(/^I should see (\d+) test suite sections? in HTML$/) do |section_count|
22
+ html_test_suites.size.should == section_count.to_i
23
+ end
@@ -1,30 +1,3 @@
1
- Given(/^I have tests in my suite from 2 classes$/) do
2
- add_run_input SAMPLE_OCUNIT_TEST
3
- add_run_input SAMPLE_KIWI_TEST
4
- end
5
-
6
- When(/^I pipe to xcpretty with "(.*?)" and specify a custom path$/) do |args|
7
- step("I pipe to xcpretty with \"#{args} --output #{custom_report_path}\"")
8
- end
9
-
10
- When(/^I pipe to xcpretty with two custom report paths$/) do
11
- step("I pipe to xcpretty with \"--report junit --output #{custom_report_path} --report junit --output #{other_custom_report_path}\"")
12
- end
13
-
14
- Then(/^I should have test reports in two custom paths$/) do
15
- step("I should have a test report at \"#{custom_report_path}\"")
16
- step("I should have a test report at \"#{other_custom_report_path}\"")
17
- end
18
-
19
- Then(/^I should have a test report in a custom path$/) do
20
- step("I should have a test report at \"#{custom_report_path}\"")
21
- end
22
-
23
- Then(/^I should have a test report at "(.*?)"$/) do |path|
24
- doc = REXML::Document.new(File.open(path, 'r').read)
25
- doc.root.should_not be_nil
26
- end
27
-
28
1
  Then(/^I should see a failed test node in my report$/) do
29
2
  junit_report_root.elements.to_a.detect do |node|
30
3
  element = node.elements.to_a.first
@@ -53,3 +26,7 @@ Then(/^I should see (\d+) test suites$/) do |count|
53
26
  suites.select {|s| s.name == 'testsuite' }.size.should == count.to_i
54
27
  end
55
28
 
29
+ Then(/^I should have a test report at "(.*?)"$/) do |path|
30
+ doc = REXML::Document.new(File.open(path, 'r').read)
31
+ doc.root.should_not be_nil
32
+ end
@@ -0,0 +1,21 @@
1
+ Given(/^I have tests in my suite from 2 classes$/) do
2
+ add_run_input SAMPLE_OCUNIT_TEST
3
+ add_run_input SAMPLE_KIWI_TEST
4
+ end
5
+
6
+ When(/^I pipe to xcpretty with "(.*?)" and specify a custom path$/) do |args|
7
+ step("I pipe to xcpretty with \"#{args} --output #{custom_report_path}\"")
8
+ end
9
+
10
+ When(/^I pipe to xcpretty with two custom "(.*?)" report paths$/) do |type|
11
+ step("I pipe to xcpretty with \"--report #{type} --output #{custom_report_path} --report #{type} --output #{other_custom_report_path}\"")
12
+ end
13
+
14
+ Then(/^I should have test reports in two custom paths$/) do
15
+ step("I should have a test report at \"#{custom_report_path}\"")
16
+ step("I should have a test report at \"#{other_custom_report_path}\"")
17
+ end
18
+
19
+ Then(/^I should have a test report in a custom path$/) do
20
+ step("I should have a test report at \"#{custom_report_path}\"")
21
+ end
@@ -1,7 +1,3 @@
1
- Given(/^the build has failed$/) do
2
- add_run_input "/Users/musalj/code/OSS/ObjectiveSugar/Example/ObjectiveSugarTests/NSArrayCategoriesTests.m:53:13: error: use of undeclared identifier 'something'"
3
- end
4
-
5
1
  When(/^I run xcpretty$/) do
6
2
  @output = `bin/xcpretty 2>&1`
7
3
  end
@@ -9,6 +9,7 @@ require 'lib/xcpretty/syntax'
9
9
  require 'rexml/document'
10
10
  require 'lib/xcpretty/formatters/formatter'
11
11
  require 'lib/xcpretty/reporters/junit'
12
+ require 'lib/xcpretty/reporters/html'
12
13
 
13
14
  include XCPretty::ANSI
14
15
 
@@ -41,6 +42,21 @@ def run_output
41
42
  @output ||= ''
42
43
  end
43
44
 
45
+ def html_report
46
+ @html_report ||= REXML::Document.new(File.open(XCPretty::HTML::FILEPATH, 'r').read.sub("<!DOCTYPE html>",""))
47
+ end
48
+
49
+ def html_report_body
50
+ html_report.root.get_elements('//body').first
51
+ end
52
+
53
+ def html_test_suites
54
+ parent = html_report_body.get_elements("//*[@id='test-suites']/").first
55
+ parent.elements.to_a.select do |e|
56
+ e.attributes['class'] && e.attributes['class'].include?('test-suite')
57
+ end
58
+ end
59
+
44
60
  def junit_report
45
61
  REXML::Document.new(File.open(XCPretty::JUnit::FILEPATH, 'r').read)
46
62
  end
@@ -72,5 +88,7 @@ After do
72
88
  @output = ""
73
89
  @custom_report_file1.unlink if @custom_report_file1
74
90
  @custom_report_file2.unlink if @custom_report_file2
91
+ @html_report = nil
75
92
  FileUtils.rm_rf(XCPretty::JUnit::FILEPATH)
93
+ FileUtils.rm_rf(XCPretty::HTML::FILEPATH)
76
94
  end
@@ -1,15 +1,5 @@
1
1
  Feature: CLI behavior
2
2
 
3
- Scenario: Xcode tests have failed
4
- Given I have a failing test in my suite
5
- When I pipe to xcpretty
6
- Then the exit status code should be 1
7
-
8
- Scenario: Xcode build has failed
9
- Given the build has failed
10
- When I pipe to xcpretty
11
- Then the exit status code should be 1
12
-
13
3
  Scenario: Starting xcpretty without any flags
14
4
  When I run xcpretty
15
5
  Then I should see the help banner
@@ -15,6 +15,8 @@ module XCPretty
15
15
  ASCII_PENDING = "P"
16
16
  ASCII_COMPLETION = ">"
17
17
 
18
+ INDENT = " "
19
+
18
20
  def format_analyze(file_name, file_path)
19
21
  format("Analyzing", file_name)
20
22
  end
@@ -56,15 +58,15 @@ module XCPretty
56
58
  end
57
59
 
58
60
  def format_failing_test(suite, test_case, reason, file)
59
- format_test("#{test_case}, #{reason}", :fail)
61
+ INDENT + format_test("#{test_case}, #{reason}", :fail)
60
62
  end
61
63
 
62
64
  def format_passing_test(suite, test_case, time)
63
- format_test("#{test_case} (#{colored_time(time)} seconds)", :pass)
65
+ INDENT + format_test("#{test_case} (#{colored_time(time)} seconds)", :pass)
64
66
  end
65
67
 
66
68
  def format_pending_test(suite, test_case)
67
- format_test("#{test_case} [PENDING]", :pending)
69
+ INDENT + format_test("#{test_case} [PENDING]", :pending)
68
70
  end
69
71
 
70
72
  def format_phase_script_execution(script_name)
@@ -0,0 +1,77 @@
1
+ module XCPretty
2
+ class HTML
3
+
4
+ include XCPretty::FormatMethods
5
+ FILEPATH = 'build/reports/tests.html'
6
+ TEMPLATE = File.expand_path('../../../../assets/report.html.erb', __FILE__)
7
+
8
+ def load_dependencies
9
+ unless @@loaded ||= false
10
+ require 'fileutils'
11
+ require 'pathname'
12
+ require 'erb'
13
+ @@loaded = true
14
+ end
15
+ end
16
+
17
+ def initialize(options)
18
+ load_dependencies
19
+ @test_suites = {}
20
+ @filepath = options[:path] || FILEPATH
21
+ @parser = Parser.new(self)
22
+ @test_count = 0
23
+ @fail_count = 0
24
+ end
25
+
26
+ def handle(line)
27
+ @parser.parse(line)
28
+ end
29
+
30
+ def format_failing_test(suite, test_case, reason, file)
31
+ add_test(suite, {:name => test_case, :failing => true,
32
+ :reason => reason, :file => file, :snippet => formatted_snippet(file)})
33
+ end
34
+
35
+ def format_passing_test(suite, test_case, time)
36
+ add_test(suite, {:name => test_case, :time => time})
37
+ end
38
+
39
+ def finish
40
+ FileUtils.mkdir_p(File.dirname(@filepath))
41
+ write_report
42
+ end
43
+
44
+ private
45
+
46
+ def formatted_snippet filepath
47
+ file, line = filepath.split(':')
48
+ f = File.open(file)
49
+ line.to_i.times { f.gets }
50
+ text = $_.strip
51
+ f.close
52
+ Syntax.highlight(text, "-f html -O style=colorful -O noclasses")
53
+ rescue
54
+ nil
55
+ end
56
+
57
+ def add_test(suite_name, data)
58
+ @test_count += 1
59
+ @test_suites[suite_name] ||= {:tests => []}
60
+ @test_suites[suite_name][:tests] << data
61
+ if data[:failing]
62
+ @test_suites[suite_name][:failing] = true
63
+ @fail_count += 1
64
+ end
65
+ end
66
+
67
+ def write_report
68
+ File.open(@filepath, 'w') do |f|
69
+ test_suites = @test_suites
70
+ fail_count = @fail_count
71
+ test_count = @test_count
72
+ erb = ERB.new(File.open(TEMPLATE, 'r').read)
73
+ f.write erb.result(binding)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,8 +1,8 @@
1
1
  module XCPretty
2
2
  class Syntax
3
3
 
4
- def self.highlight(code)
5
- pygments_available? ? pygmentize(code) : code
4
+ def self.highlight(code, options="")
5
+ pygments_available? ? pygmentize(code, options) : code
6
6
  end
7
7
 
8
8
 
@@ -13,10 +13,9 @@ module XCPretty
13
13
  @available
14
14
  end
15
15
 
16
- def self.pygmentize(code)
17
- `echo "#{code}" | pygmentize -l objc`
16
+ def self.pygmentize(code, options)
17
+ `echo "#{code}" | pygmentize -l objc #{options}`
18
18
  end
19
-
20
19
  end
21
20
  end
22
21
 
@@ -1,3 +1,3 @@
1
1
  module XCPretty
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
data/lib/xcpretty.rb CHANGED
@@ -5,28 +5,9 @@ require "xcpretty/formatters/formatter"
5
5
  require "xcpretty/formatters/simple"
6
6
  require "xcpretty/formatters/rspec"
7
7
  require "xcpretty/reporters/junit"
8
+ require "xcpretty/reporters/html"
8
9
 
9
10
  module XCPretty
10
- module ExitStatus
11
-
12
- include XCPretty::Matchers
13
-
14
- POSSIBLE_FAILURES = [
15
- FAILING_TEST_MATCHER,
16
- /\serror:\s/
17
- ]
18
-
19
- def self.code
20
- $exit_status || 0
21
- end
22
-
23
- def self.handle(text)
24
- POSSIBLE_FAILURES.detect do |failure|
25
- $exit_status = 1 if text =~ failure
26
- end
27
- end
28
-
29
- end
30
11
 
31
12
  def self.class_from_path(path)
32
13
  source = File.read(path)
@@ -67,17 +67,17 @@ module XCPretty
67
67
 
68
68
  it "formats failing tests" do
69
69
  @formatter.format_failing_test("RACCommandSpec", "enabled_signal_should_send_YES_while_executing_is_YES_and_allowsConcurrentExecution_is_YES", "expected: 1, got: 0", 'path/to/file').should ==
70
- "x enabled_signal_should_send_YES_while_executing_is_YES_and_allowsConcurrentExecution_is_YES, expected: 1, got: 0"
70
+ " x enabled_signal_should_send_YES_while_executing_is_YES_and_allowsConcurrentExecution_is_YES, expected: 1, got: 0"
71
71
  end
72
72
 
73
73
  it "formats passing tests" do
74
74
  @formatter.format_passing_test("RACCommandSpec", "_tupleByAddingObject__should_add_a_non_nil_object", "0.001").should ==
75
- ". _tupleByAddingObject__should_add_a_non_nil_object (0.001 seconds)"
75
+ " . _tupleByAddingObject__should_add_a_non_nil_object (0.001 seconds)"
76
76
  end
77
77
 
78
78
  it "formats pending tests" do
79
79
  @formatter.format_pending_test("RACCommandSpec", "_tupleByAddingObject__should_add_a_non_nil_object").should ==
80
- "P _tupleByAddingObject__should_add_a_non_nil_object [PENDING]"
80
+ " P _tupleByAddingObject__should_add_a_non_nil_object [PENDING]"
81
81
  end
82
82
 
83
83
  it "formats Phase Script Execution" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcpretty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marin Usalj
@@ -9,62 +9,62 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-20 00:00:00.000000000 Z
12
+ date: 2014-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ~>
19
19
  - !ruby/object:Gem::Version
20
20
  version: '1.3'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ~>
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.3'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rake
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - '>='
33
33
  - !ruby/object:Gem::Version
34
34
  version: '0'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - '>='
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rspec
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ">="
46
+ - - '>='
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ">="
53
+ - - '>='
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: cucumber
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">="
60
+ - - '>='
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">="
67
+ - - '>='
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  description: "\n Xcodebuild formatter designed to be piped with `xcodebuild`,\n and
@@ -78,22 +78,26 @@ executables:
78
78
  extensions: []
79
79
  extra_rdoc_files: []
80
80
  files:
81
- - ".gitignore"
82
- - ".kick"
83
- - ".travis.yml"
81
+ - .gitignore
82
+ - .kick
83
+ - .travis.yml
84
84
  - CHANGELOG.md
85
85
  - CONTRIBUTING.md
86
86
  - Gemfile
87
87
  - LICENSE.txt
88
88
  - README.md
89
89
  - Rakefile
90
+ - assets/report.html.erb
90
91
  - bin/xcpretty
91
92
  - features/custom_formatter.feature
92
93
  - features/fixtures/xcodebuild.log
94
+ - features/html_report.feature
93
95
  - features/junit_report.feature
94
96
  - features/simple_format.feature
95
97
  - features/steps/formatting_steps.rb
98
+ - features/steps/html_steps.rb
96
99
  - features/steps/junit_steps.rb
100
+ - features/steps/report_steps.rb
97
101
  - features/steps/xcpretty_steps.rb
98
102
  - features/support/env.rb
99
103
  - features/test_format.feature
@@ -105,6 +109,7 @@ files:
105
109
  - lib/xcpretty/formatters/simple.rb
106
110
  - lib/xcpretty/parser.rb
107
111
  - lib/xcpretty/printer.rb
112
+ - lib/xcpretty/reporters/html.rb
108
113
  - lib/xcpretty/reporters/junit.rb
109
114
  - lib/xcpretty/syntax.rb
110
115
  - lib/xcpretty/version.rb
@@ -133,27 +138,30 @@ require_paths:
133
138
  - lib
134
139
  required_ruby_version: !ruby/object:Gem::Requirement
135
140
  requirements:
136
- - - ">="
141
+ - - '>='
137
142
  - !ruby/object:Gem::Version
138
143
  version: 1.8.7
139
144
  required_rubygems_version: !ruby/object:Gem::Requirement
140
145
  requirements:
141
- - - ">="
146
+ - - '>='
142
147
  - !ruby/object:Gem::Version
143
148
  version: '0'
144
149
  requirements: []
145
150
  rubyforge_project:
146
- rubygems_version: 2.2.0
151
+ rubygems_version: 2.0.3
147
152
  signing_key:
148
153
  specification_version: 4
149
154
  summary: xcodebuild formatter done right
150
155
  test_files:
151
156
  - features/custom_formatter.feature
152
157
  - features/fixtures/xcodebuild.log
158
+ - features/html_report.feature
153
159
  - features/junit_report.feature
154
160
  - features/simple_format.feature
155
161
  - features/steps/formatting_steps.rb
162
+ - features/steps/html_steps.rb
156
163
  - features/steps/junit_steps.rb
164
+ - features/steps/report_steps.rb
157
165
  - features/steps/xcpretty_steps.rb
158
166
  - features/support/env.rb
159
167
  - features/test_format.feature