golden_rose 0.1.0 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module GoldenRose
2
+ class Info
3
+ def initialize(parsed_info)
4
+ @parsed_info = parsed_info
5
+ end
6
+
7
+ def build_log_path
8
+ @parsed_info['Actions'].first['BuildResult']['LogPath']
9
+ end
10
+ end
11
+ end
@@ -2,57 +2,55 @@
2
2
  # from zip file or folder
3
3
 
4
4
  require "plist"
5
- require "zip"
6
- require "securerandom"
7
5
 
8
6
  module GoldenRose
9
7
  class Parser
10
- FILE_NAME = "action_TestSummaries.plist"
11
-
12
- attr_accessor :parsed_plist, :folder_path
8
+ attr_accessor :folder_path
9
+ attr_reader :parsed_test_summaries_plist, :parsed_info_plist, :parsed_build_logs
13
10
 
14
11
  def initialize(folder_path)
15
12
  @folder_path = folder_path
16
13
  end
17
14
 
18
15
  def parse!
19
- archive? ? open_zip : open_directory
20
- raise GeneratingError, "File 'TestSummaries.plist' was not found in the folder." unless @plist_file_path
21
- @parsed_plist = Plist::parse_xml(@plist_file_path)
22
- File.delete(@plist_file_path) if archive?
23
- raise GeneratingError, "Could not parse plist correctly." unless parsed_plist
24
- parsed_plist
16
+ read_plists
17
+ parse_plists
18
+ parse_build_logs
19
+ delete_extracted_files
20
+ self
21
+ end
25
22
 
26
- rescue Zip::Error, Errno::ENOENT
27
- raise GeneratingError, "Could not open the folder."
23
+ def source_type
24
+ archive? ? :zip : :dir
28
25
  end
29
26
 
30
27
  private
31
28
 
32
- def open_directory
33
- @plist_file_path = Dir.glob("#{folder_path}/**/#{FILE_NAME}").first
29
+ def read_plists
30
+ @test_summaries_resource = FileResource.new(:test_summaries_plist, source_type, folder_path)
31
+ @info_resource = FileResource.new(:info_plist, source_type, folder_path)
32
+ end
33
+
34
+ def parse_plists
35
+ @parsed_test_summaries_plist = @test_summaries_resource.path ? Plist.parse_xml(@test_summaries_resource.path) : nil
36
+ puts("Could not parse test_summaries.plist correctly") unless @parsed_test_summaries_plist
37
+ @parsed_info_plist = Plist.parse_xml(@info_resource.path)
38
+ fail GeneratingError, 'Could not parse Info.plist correctly.' unless @parsed_info_plist
34
39
  end
35
40
 
36
- def open_zip
37
- Zip::File.open(folder_path) do |zip_file|
38
- entry = zip_file.find do |entry|
39
- file_name = File.basename(entry.name)
40
- test_summaries?(file_name)
41
- end
42
- if entry
43
- uuid = ::SecureRandom.uuid
44
- @plist_file_path = File.join(GoldenRose::root, "/source_#{uuid}.plist")
45
- entry.extract(@plist_file_path)
46
- end
47
- end
41
+ def parse_build_logs
42
+ build_log_path = Info.new(@parsed_info_plist).build_log_path
43
+ @build_log_resource = FileResource.new(:build_log, source_type, folder_path, build_log_path)
44
+ @parsed_build_logs = GoldenRose::BuildLog::Parser.new(@build_log_resource.path).parse!
48
45
  end
49
46
 
50
- def test_summaries?(file_name)
51
- file_name == FILE_NAME
47
+ def delete_extracted_files
48
+ [@test_summaries_resource, @info_resource, @build_log_resource]
49
+ .map(&:delete_extracted_file)
52
50
  end
53
51
 
54
52
  def archive?
55
53
  !(folder_path =~ /\.(?:rar|zip|tar.gz)$/).nil?
56
54
  end
57
55
  end
58
- end
56
+ end
@@ -3,65 +3,15 @@
3
3
 
4
4
  module GoldenRose
5
5
  class ResultsFilterer
6
- attr_accessor :parsed_plist, :results
6
+ attr_accessor :parsed_plist
7
7
 
8
8
  def initialize(parsed_plist)
9
9
  @parsed_plist = parsed_plist
10
10
  end
11
11
 
12
12
  def filter!
13
- raise GeneratingError, "Testable summaries not present." unless testable_summaries
14
- raise GeneratingError, "Tests not present." unless tests
15
- @results = Results.new(execution_details, items)
13
+ return nil if @parsed_plist.nil?
14
+ ExecutionDetails.new(@parsed_plist)
16
15
  end
17
-
18
- private
19
-
20
- def iterate_subtests(items)
21
- items.map do |subtest|
22
- child_subtests = subtest["Subtests"]
23
- next if child_subtests && child_subtests.empty?
24
-
25
- if child_subtests
26
- ParentItem.new(subtest).tap do |item|
27
- item.subtests = iterate_subtests(child_subtests)
28
- end.to_h
29
- else
30
- ChildItem.new(subtest).to_h
31
- end
32
- end.compact
33
- end
34
-
35
- def items
36
- @items ||= compact_results(iterate_subtests(tests))
37
- end
38
-
39
- # This method simplify results structure,
40
- # it sets only one level of nesting
41
- # by leaving parents only with collection as child
42
- def compact_results(items)
43
- items.map do |subtest|
44
- subtests = subtest[:subtests]
45
- if subtests.size > 1
46
- subtests
47
- elsif subtests.size == 1
48
- compact_results(subtests)
49
- end
50
- end.flatten.compact
51
- end
52
-
53
- def execution_details
54
- ExecutionDetails.new(parsed_plist, items)
55
- end
56
-
57
- def testable_summaries
58
- parsed_plist['TestableSummaries']
59
- end
60
-
61
- def tests
62
- testable_summaries.first['Tests']
63
- end
64
-
65
- class Results < Struct.new(:details, :items); end
66
16
  end
67
- end
17
+ end
@@ -0,0 +1,24 @@
1
+ %ol#collapsible
2
+ - @build_logs.each do |build_target|
3
+ %li
4
+ %label(for="#{build_target.target_name}")
5
+ = "Build target #{build_target.target_name}"
6
+ %input(type="checkbox" id="#{build_target.target_name}")
7
+ %ol
8
+ - build_target.sections.each do |build_section|
9
+ %li
10
+ %span{class: (build_section.success? ? 'build-log-success' : 'build-log-error')}
11
+ = build_section.name
12
+ - if build_section.file?
13
+ %span.file
14
+ = build_section.file
15
+ %span.path
16
+ = "...in #{build_section.directory}"
17
+ - elsif !build_section.file? && build_section.path?
18
+ %span.path
19
+ = "#{build_section.path}"
20
+ - if build_section.errors?
21
+ %ul
22
+ - build_section.errors.each do |build_error|
23
+ %li.build-log-error.error-text
24
+ = build_error
@@ -0,0 +1,5 @@
1
+ #footer
2
+ Made with
3
+ %span.heart ❤
4
+ by
5
+ %a(title="PGS Software" href="https://pgs-soft.com")PGS Software
@@ -0,0 +1,9 @@
1
+ - (activity_summaries || []).each do |activity_summary|
2
+ %tr
3
+ %td
4
+ = activity_summary.content.start_time
5
+ %td
6
+ = activity_summary.content.finish_time
7
+ %td
8
+ = activity_summary.content.title
9
+ = render_subactivities(activity_summary)
@@ -0,0 +1,52 @@
1
+ - if @details.testable_summaries.size > 1
2
+ %ul.tab-nd.tests-tabs
3
+ - @details.testable_summaries.each.with_index do |testable_summary, idx|
4
+ %li
5
+ %a.tablinks-nd{href: "javascript:void(0)", onclick: "openTabNd(event, 'testable-summary-#{idx}')"}
6
+ = testable_summary.name
7
+ - @details.testable_summaries.each.with_index do |testable_summary, idx|
8
+ %div{id: "testable-summary-#{idx}", class: 'tabcontent-nd'}
9
+ %h1
10
+ = testable_summary.name
11
+ #details
12
+ #{testable_summary.total_tests_count} tests run on
13
+ %strong
14
+ #{@details.model_name} iOS #{@details.os_version}
15
+ with
16
+ %strong
17
+ #{testable_summary.passing_count} passing
18
+ and
19
+ %strong
20
+ #{testable_summary.failures_count} failing
21
+ in #{testable_summary.formatted_time}
22
+ #results
23
+ %ol#collapsible
24
+ - testable_summary.items.each do |item|
25
+ %li
26
+ %label(for="#{item[:node_id]}")
27
+ = item[:name]
28
+ - if item[:failures_count]
29
+ %span.failures-count
30
+ = "Failures: "
31
+ = item[:failures_count]
32
+ %input(type="checkbox" id="#{item[:node_id]}")
33
+ %ol
34
+ - (item[:subtests] || []).each do |subtest|
35
+ %li
36
+ %span(class="#{subtest[:status]}")
37
+ - if subtest[:has_activity_summaries]
38
+ %a.link{:href => "test_logs/#{subtest[:uuid]}.html", :target => "_blank"}
39
+ = subtest[:name]
40
+ - else
41
+ = subtest[:name]
42
+ %span.time
43
+ = subtest[:time]
44
+ - if subtest[:failures]
45
+ - subtest[:failures].each do |failure|
46
+ .failure-message
47
+ = failure[:message]
48
+ .failure-details
49
+ #{failure[:file_name]}:#{failure[:line_number]}
50
+ - if subtest[:instance].screenshot
51
+ %a{:href => "#{subtest[:instance].screenshot}"}
52
+ %img.screenshot-thumb{alt: "#{subtest[:instance].screenshot}", src: "#{subtest[:instance].screenshot}"}/
@@ -0,0 +1,48 @@
1
+ :javascript
2
+ function openTab(evt, tabId) {
3
+ var i, tabcontent, tablinks;
4
+
5
+ // Get all elements with class="tabcontent" and hide them
6
+ tabcontent = document.getElementsByClassName("tabcontent");
7
+ for (i = 0; i < tabcontent.length; i++) {
8
+ tabcontent[i].style.display = "none";
9
+ }
10
+
11
+ // Get all elements with class="tablinks" and remove the class "active"
12
+ tablinks = document.getElementsByClassName("tablinks");
13
+ for (i = 0; i < tablinks.length; i++) {
14
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
15
+ }
16
+
17
+ // Show the current tab, and add an "active" class to the link that opened the tab
18
+ document.getElementById(tabId).style.display = "block";
19
+ evt.currentTarget.className += " active";
20
+ }
21
+
22
+ function openTabNd(evt, tabId) {
23
+ var i, tabcontent, tablinks;
24
+
25
+ // Get all elements with class="tabcontent" and hide them
26
+ tabcontent = document.getElementsByClassName("tabcontent-nd");
27
+ for (i = 0; i < tabcontent.length; i++) {
28
+ tabcontent[i].style.display = "none";
29
+ }
30
+
31
+ // Get all elements with class="tablinks" and remove the class "active"
32
+ tablinks = document.getElementsByClassName("tablinks-nd");
33
+ for (i = 0; i < tablinks.length; i++) {
34
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
35
+ }
36
+
37
+ // Show the current tab, and add an "active" class to the link that opened the tab
38
+ document.getElementById(tabId).style.display = "block";
39
+ evt.currentTarget.className += " active";
40
+ }
41
+
42
+
43
+ // Get the element with id="defaultOpen" and click on it
44
+ document.getElementById("default-open").click();
45
+ var firstTablinkNd = document.getElementsByClassName("tablinks-nd")[0];
46
+ if (firstTablinkNd) {
47
+ firstTablinkNd.click();
48
+ }
@@ -0,0 +1,203 @@
1
+ :css
2
+ body { font-family: 'Source Sans Pro', sans-serif; }
3
+ #container { max-width: 1170px; margin: auto;}
4
+ h1 {
5
+ color: #ff7726;
6
+ font-size: 48px;
7
+ font-weight: 600;
8
+ line-height: 18px;
9
+ text-align: left;
10
+ margin: 73px 0 24px 0;
11
+ }
12
+ #details {
13
+ color: #4e4e4e;
14
+ margin: 0 0 60px 0;
15
+ font-size: 18px;
16
+ line-height: 18px;
17
+ }
18
+ .no-results-info {
19
+ text-align: center;
20
+ font-size: 28pt;
21
+ color: grey;
22
+ margin-top: 80px;
23
+ }
24
+ #results { margin-right: 34px; }
25
+ input[type=checkbox], input[type=checkbox] + ol > li { display: none; }
26
+ input[type=checkbox]:checked + ol > li { display: block; }
27
+ ol, li { position: relative; }
28
+ ol { margin: 0; padding: 0 0 0 34px; }
29
+ ol#collapsible { padding: 0; }
30
+ ol::before {
31
+ content: "▶";
32
+ position: absolute;
33
+ top: -33px; left: 15px;
34
+ font-size: 10px;
35
+ color: #ff7726;
36
+ }
37
+ ol#collapsible::before { display: none; }
38
+ input[type=checkbox]:checked + ol::before { content: "▼"; }
39
+ li { list-style: none; }
40
+ label { cursor: pointer; }
41
+ label, .success, .failure {
42
+ padding: 13.5px 0 13.5px 34px;
43
+ width: auto;
44
+ display: block;
45
+ background: #f6f6f6;
46
+ border-bottom: 3px #fff solid;
47
+ color: #4e4e4e;
48
+ letter-spacing: -0.32px;
49
+ line-height: 18px;
50
+ }
51
+ .time, .failures-count {
52
+ float: right;
53
+ width: 90px;
54
+ }
55
+ .failures-count { color: #a80000; }
56
+ .success {
57
+ background: #ccffcc;
58
+ color: #005e37;
59
+ padding: 8.5px 0 8.5px 34px;
60
+ font-weight: 400;
61
+ }
62
+ .failure {
63
+ background: #ffcfcf;
64
+ color: #a80000;
65
+ }
66
+ .failure-message, .failure-details {
67
+ clear: both;
68
+ display: block;
69
+ font-size: 14px;
70
+ letter-spacing: -0.28px;
71
+ margin-right: 34px;
72
+ }
73
+ #build-logs-id {
74
+ margin-right: 34px;
75
+ padding-top: 80px;
76
+ }
77
+ #build-logs-id .build-log-success:before {
78
+ content:'✓';
79
+ font-weight: bold;
80
+ display:inline-block;
81
+ vertical-align: top;
82
+ line-height: 1em;
83
+ width: 1em;
84
+ height:1em;
85
+ margin-right: 0.3em;
86
+ text-align: center;
87
+ color: #FFF;
88
+ background-color: green;
89
+ -moz-border-radius: 0.5em;
90
+ -webkit-border-radius: 0.5em;
91
+ border-radius: 0.5em;
92
+ margin-top: 2px;
93
+ }
94
+ #build-logs-id .build-log-error:before {
95
+ content:'✗';
96
+ font-weight: bold;
97
+ display:inline-block;
98
+ line-height: 1em;
99
+ vertical-align: top;
100
+ width: 1em;
101
+ height:1em;
102
+ margin-right: 0.3em;
103
+ text-align: center;
104
+ color: #FFF;
105
+ background-color: red;
106
+ -moz-border-radius: 0.5em;
107
+ -webkit-border-radius: 0.5em;
108
+ border-radius: 0.5em;
109
+ margin-top: 2px;
110
+ }
111
+ .error-text {
112
+ color: red;
113
+ font-size: 14px;
114
+ }
115
+ #build-logs-id .path {
116
+ color: #737373;
117
+ font-family: Courier New;
118
+ font-size: 14px;
119
+ }
120
+ #build-logs-id .file {
121
+ font-size: 14px;
122
+ }
123
+ #footer {
124
+ color: #4e4e4e;
125
+ font-size: 14px;
126
+ margin-top: 40px;
127
+ }
128
+ .heart { color: #ff7726; }
129
+ #footer a { color: #ff7726; text-decoration: none; }
130
+ /* TABS styles - BEGIN ========================== */
131
+ ul.tab, ul.tab-nd {
132
+ list-style-type: none;
133
+ margin: 0;
134
+ padding: 0;
135
+ overflow: hidden;
136
+ }
137
+ /* Float the list items side by side */
138
+ ul.tab li, ul.tab-nd li {float: left;}
139
+ /* Style the links inside the list items */
140
+ ul.tab li a {
141
+ display: inline-block;
142
+ color: black;
143
+ text-align: center;
144
+ padding: 14px 35px;
145
+ text-decoration: none;
146
+ transition: 0.3s;
147
+ font-size: 17px;
148
+ font-weight: bold;
149
+ }
150
+ ul.tab-nd li a {
151
+ display: inline-block;
152
+ color: black;
153
+ text-align: center;
154
+ padding: 10px 30px;
155
+ text-decoration: none;
156
+ transition: 0.3s;
157
+ font-size: 17px;
158
+ font-weight: bold;
159
+ }
160
+ ul.tab-nd {
161
+ display: inline-block;
162
+ position: relative;
163
+ left: -13px;
164
+ }
165
+ /* Create an active/current tablink class */
166
+ ul.tab li a:focus, ul.tab li a.active, ul.tab li a:hover {
167
+ background-color: #FF7726;
168
+ color: #FFF;
169
+ }
170
+ ul.tab-nd li a:focus, ul.tab-nd li a.active, ul.tab-nd li a:hover {
171
+ border-width: 1px 1px 0 1px;
172
+ }
173
+ ul.tab-nd li a:hover {
174
+ background-color: #FF7726;
175
+ color: #FFF;
176
+ }
177
+ ul.tab-nd li a {
178
+ background-color: #FFF;
179
+ color: black;
180
+ border-color: #FF7726;
181
+ border-width: 0 0 1px 0;
182
+ border-style: solid;
183
+ }
184
+ /* Style the tab content */
185
+ .tabcontent {
186
+ display: none;
187
+ padding: 6px 12px;
188
+ border-top: none;
189
+ }
190
+ .tests-tabs {
191
+ top: 50px;
192
+ }
193
+ /* TABS styles - END ===================== */
194
+
195
+ .link {
196
+ text-decoration : none;
197
+ color:inherit;
198
+ }
199
+
200
+ .screenshot-thumb {
201
+ height:100px;
202
+ width:auto;
203
+ }
@@ -0,0 +1,101 @@
1
+ :css
2
+ /*** Table Styles **/
3
+ #test-logs .table-fill {
4
+ background: white;
5
+ border-radius:3px;
6
+ border-collapse: collapse;
7
+ margin: auto;
8
+ padding:0px;
9
+ width: 100%;
10
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
11
+ animation: float 5s infinite;
12
+ }
13
+ #test-logs th {
14
+ color: #FFFFFF;
15
+ background: #FF7726;
16
+ border-bottom:1px solid #ff7e33;
17
+ border-right: 1px solid #ff7e33;
18
+ font-size:16px;
19
+ font-weight: 900;
20
+ padding:6px;
21
+ text-align:left;
22
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
23
+ vertical-align:middle;
24
+ }
25
+
26
+ #test-logs th:first-child {
27
+ border-top-left-radius:3px;
28
+ }
29
+
30
+ #test-logs th:last-child {
31
+ border-top-right-radius:3px;
32
+ border-right:none;
33
+ }
34
+
35
+ #test-logs tr {
36
+ border-top: 1px solid #C1C3D1;
37
+ border-bottom-: 1px solid #C1C3D1;
38
+ color:#666B85;
39
+ font-size:16px;
40
+ font-weight:normal;
41
+ text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
42
+ }
43
+
44
+ #test-logs tr:first-child {
45
+ border-top:none;
46
+ }
47
+
48
+ #test-logs tr:last-child {
49
+ border-bottom:none;
50
+ }
51
+
52
+ #test-logs tr:nth-child(odd) td {
53
+ background:#ffefe6;
54
+ }
55
+
56
+ #test-logs tr:last-child td:first-child {
57
+ border-bottom-left-radius:3px;
58
+ }
59
+
60
+ #test-logs tr:last-child td:last-child {
61
+ border-bottom-right-radius:3px;
62
+ }
63
+
64
+ #test-logs td {
65
+ background:#FFFFFF;
66
+ padding:4px 6px;
67
+ text-align:left;
68
+ vertical-align:middle;
69
+ font-weight:300;
70
+ font-size:16px;
71
+ text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
72
+ border-right: 1px solid #C1C3D1;
73
+ }
74
+
75
+ #test-logs td:last-child {
76
+ border-right: 0px;
77
+ }
78
+
79
+ #test-logs th.text-left {
80
+ text-align: left;
81
+ }
82
+
83
+ #test-logs th.text-center {
84
+ text-align: center;
85
+ }
86
+
87
+ #test-logs th.text-right {
88
+ text-align: right;
89
+ }
90
+
91
+ #test-logs td.text-left {
92
+ text-align: left;
93
+ }
94
+
95
+ #test-logs td.text-center {
96
+ text-align: center;
97
+ }
98
+
99
+ #test-logs td.text-right {
100
+ text-align: right;
101
+ }