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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +10 -5
- data/golden_rose.gemspec +3 -1
- data/lib/golden_rose/build_log/build_section.rb +56 -0
- data/lib/golden_rose/build_log/build_target.rb +21 -0
- data/lib/golden_rose/build_log/parser.rb +40 -0
- data/lib/golden_rose/build_log.rb +3 -0
- data/lib/golden_rose/child_item.rb +91 -9
- data/lib/golden_rose/class_configurator.rb +49 -0
- data/lib/golden_rose/cli/app.rb +12 -3
- data/lib/golden_rose/execution_details.rb +22 -42
- data/lib/golden_rose/file_resource.rb +98 -0
- data/lib/golden_rose/generators/html_format.rb +70 -7
- data/lib/golden_rose/info.rb +11 -0
- data/lib/golden_rose/parser.rb +28 -30
- data/lib/golden_rose/results_filterer.rb +4 -54
- data/lib/golden_rose/templates/_build_logs.haml +24 -0
- data/lib/golden_rose/templates/_footer.haml +5 -0
- data/lib/golden_rose/templates/_test_logs.haml +9 -0
- data/lib/golden_rose/templates/_tests.haml +52 -0
- data/lib/golden_rose/templates/assets/javascript/main.js.haml +48 -0
- data/lib/golden_rose/templates/assets/styles/main.css.haml +203 -0
- data/lib/golden_rose/templates/assets/styles/test_logs.css.haml +101 -0
- data/lib/golden_rose/templates/index.haml +23 -116
- data/lib/golden_rose/templates/test_logs/index.haml +38 -0
- data/lib/golden_rose/testable_summary.rb +90 -0
- data/lib/golden_rose/version.rb +1 -1
- data/lib/golden_rose/xcactivitylog_reader.rb +54 -0
- data/lib/golden_rose.rb +20 -5
- metadata +51 -6
data/lib/golden_rose/parser.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
read_plists
|
17
|
+
parse_plists
|
18
|
+
parse_build_logs
|
19
|
+
delete_extracted_files
|
20
|
+
self
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
23
|
+
def source_type
|
24
|
+
archive? ? :zip : :dir
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
31
28
|
|
32
|
-
def
|
33
|
-
@
|
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
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
51
|
-
|
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
|
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
|
-
|
14
|
-
|
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,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
|
+
}
|