nukumber 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/nukumber +14 -0
- data/lib/nukumber.rb +29 -0
- data/lib/nukumber/builder.rb +104 -0
- data/lib/nukumber/cli.rb +101 -0
- data/lib/nukumber/errors.rb +18 -0
- data/lib/nukumber/helpers.rb +5 -0
- data/lib/nukumber/hooks.rb +23 -0
- data/lib/nukumber/model.rb +9 -0
- data/lib/nukumber/model/background.rb +17 -0
- data/lib/nukumber/model/examples.rb +16 -0
- data/lib/nukumber/model/feature.rb +33 -0
- data/lib/nukumber/model/feature_element.rb +47 -0
- data/lib/nukumber/model/scenario.rb +20 -0
- data/lib/nukumber/model/scenario_outline.rb +21 -0
- data/lib/nukumber/model/step.rb +15 -0
- data/lib/nukumber/model/table.rb +53 -0
- data/lib/nukumber/reporter.rb +54 -0
- data/lib/nukumber/reporter/colour.rb +138 -0
- data/lib/nukumber/reporter/html.rb +143 -0
- data/lib/nukumber/reporter/html_css.rb +169 -0
- data/lib/nukumber/reporter/mono.rb +19 -0
- data/lib/nukumber/runtime.rb +237 -0
- data/lib/nukumber/version.rb +7 -0
- metadata +117 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class ScenarioOutline < FeatureElement
|
6
|
+
attr_accessor :tags, :examples
|
7
|
+
|
8
|
+
def initialize(name, line, description, feature)
|
9
|
+
super(name, line, description, feature)
|
10
|
+
@tags = []
|
11
|
+
@examples = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def keyword
|
15
|
+
'Scenario Outline'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Step
|
6
|
+
attr_reader :name, :line, :keyword, :element, :args
|
7
|
+
|
8
|
+
def initialize(name, line, keyword, element, args)
|
9
|
+
@name, @line, @keyword, @element, @args = name, line, keyword, element, args
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Table
|
6
|
+
attr_reader :headers, :rows, :lengths
|
7
|
+
|
8
|
+
def initialize(rows)
|
9
|
+
@headers, @rows, @lengths, @empty = [], [], [], true
|
10
|
+
return if rows.nil?
|
11
|
+
@empty = false
|
12
|
+
rows.each_with_index do |r, i|
|
13
|
+
if i == 0
|
14
|
+
@headers = r.cells.map { |c| c.to_s }
|
15
|
+
@lengths = @headers.map { |h| h.length }
|
16
|
+
else
|
17
|
+
row = []
|
18
|
+
r.cells.each_with_index do |c, j|
|
19
|
+
row << c.to_s
|
20
|
+
@lengths[j] = [ c.to_s.length, @lengths[j] ].max
|
21
|
+
end
|
22
|
+
@rows << row
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?
|
28
|
+
@empty
|
29
|
+
end
|
30
|
+
|
31
|
+
def row_count
|
32
|
+
@rows.size
|
33
|
+
end
|
34
|
+
|
35
|
+
def row_hash(n)
|
36
|
+
out = {}
|
37
|
+
@rows[n].each_with_index do |val,i|
|
38
|
+
out[@headers[i]] = val
|
39
|
+
end
|
40
|
+
out
|
41
|
+
end
|
42
|
+
|
43
|
+
def all_row_hashes
|
44
|
+
out = []
|
45
|
+
row_count.times { |i| out << row_hash(i) }
|
46
|
+
out
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Nukumber
|
4
|
+
|
5
|
+
module Reporter
|
6
|
+
|
7
|
+
class Abstract
|
8
|
+
|
9
|
+
def initialize(out)
|
10
|
+
@outstream = out
|
11
|
+
end
|
12
|
+
|
13
|
+
public
|
14
|
+
|
15
|
+
def begin_feature(feature) end
|
16
|
+
def begin_element(element) end
|
17
|
+
def undefined_element(element) end
|
18
|
+
def print_step(step, status) end
|
19
|
+
def print_example(table, row, status) end
|
20
|
+
def error(exception, element) end
|
21
|
+
def final_report(passed, failed, pending, undefined) end
|
22
|
+
def print_skeleton_code(elements) end
|
23
|
+
def terminate() end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def filtered_backtrace(btrace)
|
27
|
+
|
28
|
+
filters = [
|
29
|
+
/\/lib\d*\/ruby\//,
|
30
|
+
/bin\//,
|
31
|
+
/gems\//,
|
32
|
+
/spec\/spec_helper\.rb/,
|
33
|
+
/lib\/rspec\/(core|expectations|matchers|mocks)/
|
34
|
+
]
|
35
|
+
|
36
|
+
filtered = []
|
37
|
+
btrace.each do |l|
|
38
|
+
filter = false
|
39
|
+
filters.each do |b|
|
40
|
+
if l.match b
|
41
|
+
filter = true
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
filtered << l unless filter
|
46
|
+
end
|
47
|
+
filtered
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Reporter
|
4
|
+
|
5
|
+
class Colour < Abstract
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
INDENT = ' '
|
10
|
+
|
11
|
+
def puts_empty_line
|
12
|
+
@outstream.puts "\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
def puts_colour_indent(text, colour_code)
|
16
|
+
out = "\e[#{colour_code}m#{text}\e[0m"
|
17
|
+
@indent.times { out = INDENT + out }
|
18
|
+
@outstream.puts out
|
19
|
+
end
|
20
|
+
|
21
|
+
def puts_red(text) puts_colour_indent(text, 31) end
|
22
|
+
def puts_green(text) puts_colour_indent(text, 32) end
|
23
|
+
def puts_yellow(text) puts_colour_indent(text, 33) end
|
24
|
+
def puts_cyan(text) puts_colour_indent(text, 36) end
|
25
|
+
def puts_grey(text) puts_colour_indent(text, 37) end
|
26
|
+
|
27
|
+
def puts_status(text, status)
|
28
|
+
puts_green text if status == :passed
|
29
|
+
puts_red text if status == :failed
|
30
|
+
puts_cyan text if status == :pending
|
31
|
+
puts_cyan text if status == :outline
|
32
|
+
puts_yellow text if status == :undefined
|
33
|
+
end
|
34
|
+
|
35
|
+
def table_row_printable(table, row)
|
36
|
+
if row.nil? or row < 0
|
37
|
+
array = table.headers
|
38
|
+
else
|
39
|
+
array = table.rows[row]
|
40
|
+
end
|
41
|
+
str = "|"
|
42
|
+
array.each_with_index do |val, i|
|
43
|
+
str += " %-#{table.lengths[i]}s |" % [val]
|
44
|
+
end
|
45
|
+
str
|
46
|
+
end
|
47
|
+
|
48
|
+
public
|
49
|
+
|
50
|
+
def begin_feature(feature)
|
51
|
+
@indent = 0
|
52
|
+
puts_cyan "#{feature.keyword}: #{feature.name}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def begin_element(element)
|
56
|
+
@indent = 1
|
57
|
+
puts_cyan "#{element.keyword}: #{element.name}"
|
58
|
+
puts_grey "#{element.feature.file_path.split('/').last}:#{element.line}"
|
59
|
+
if element.is_a? Nukumber::Model::ScenarioOutline
|
60
|
+
element.steps.each { |step| print_step(step, :outline) }
|
61
|
+
@indent = 3
|
62
|
+
puts_cyan table_row_printable(element.examples.table, nil)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def undefined_element(element)
|
67
|
+
@indent = 1
|
68
|
+
puts_yellow "#{element.keyword}: #{element.name}"
|
69
|
+
puts_grey "#{element.feature.file_path.split('/').last}:#{element.line}"
|
70
|
+
element.steps.each { |step| print_step(step, :undefined) }
|
71
|
+
if element.is_a? Nukumber::Model::ScenarioOutline
|
72
|
+
@indent = 3
|
73
|
+
(0..element.examples.table.row_count).each do |i|
|
74
|
+
puts_status(table_row_printable(element.examples.table, i - 1), :undefined)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_step(step, status = :passed)
|
80
|
+
@indent = 2
|
81
|
+
puts_status("#{step.keyword}#{step.name}", status)
|
82
|
+
@indent = 3
|
83
|
+
unless step.args.empty?
|
84
|
+
(0..step.args.row_count).each do |i|
|
85
|
+
puts_status(table_row_printable(step.args, i - 1), status)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def print_example(table, row, status = :passed)
|
91
|
+
puts_status(table_row_printable(table, row), status)
|
92
|
+
end
|
93
|
+
|
94
|
+
def error(exception, element)
|
95
|
+
@indent = 2
|
96
|
+
puts_red exception.message
|
97
|
+
@indent = 3
|
98
|
+
filtered_backtrace(exception.backtrace).each { |l| puts_red l }
|
99
|
+
puts_red "#{element.feature.file_path}:#{element.line}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def final_report(passed, failed, pending, undefined)
|
103
|
+
@indent = 0
|
104
|
+
puts_green "\n#{passed.size} test#{passed.size == 1 ? '' : 's'} passed"
|
105
|
+
puts_cyan "#{pending.size} test#{pending.size == 1 ? '' : 's'} pending"
|
106
|
+
puts_yellow "#{undefined.size} test#{undefined.size == 1 ? '' : 's'} undefined"
|
107
|
+
puts_red "#{failed.size} test#{failed.size == 1 ? '' : 's'} failed#{failed.size == 0 ? '' : ':'}"
|
108
|
+
@indent = 1
|
109
|
+
failed.each { |f| puts_red "#{f.feature.file_path}:#{f.line} # #{f.name}" }
|
110
|
+
end
|
111
|
+
|
112
|
+
def print_skeleton_code(elements)
|
113
|
+
@indent = 0
|
114
|
+
puts_empty_line
|
115
|
+
puts_yellow "Build Nukumber test definitions something like this:"
|
116
|
+
puts_empty_line
|
117
|
+
elements.each do |element|
|
118
|
+
@indent = 0
|
119
|
+
puts_yellow "def #{element.shortsym}"
|
120
|
+
@indent = 1
|
121
|
+
puts_yellow "# $args is a Hash (or Array of Hashes) representing any step arguments"
|
122
|
+
puts_yellow "# $example is a Hash representing the current row for this outline" if element.is_a? Nukumber::Model::ScenarioOutline
|
123
|
+
puts_yellow "# Now precede each \"pass\" line below with the code it describes..."
|
124
|
+
puts_yellow "pending"
|
125
|
+
element.steps.each do |step|
|
126
|
+
puts_yellow "pass \"#{step.name.gsub('"', '\"')}\""
|
127
|
+
end
|
128
|
+
@indent = 0
|
129
|
+
puts_yellow "end"
|
130
|
+
puts_empty_line
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Reporter
|
4
|
+
|
5
|
+
class Html < Abstract
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def add_row(nuke_table, row, table_node, status_class = nil)
|
10
|
+
if row.nil? or row < 0
|
11
|
+
array, tag = nuke_table.headers, 'th'
|
12
|
+
else
|
13
|
+
array, tag = nuke_table.rows[row], 'td'
|
14
|
+
end
|
15
|
+
row = node("tr", table_node)
|
16
|
+
row['class'] = status_class if status_class
|
17
|
+
array.each { |val| node(tag, row, {}, val) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def node(type, parent = nil, attributes = {}, content = nil)
|
21
|
+
div = Nokogiri::XML::Node.new(type, @doc)
|
22
|
+
attributes.each_pair{ |k, v| div[k.to_s] = v }
|
23
|
+
div.content = content unless content.nil?
|
24
|
+
parent.add_child(div) unless parent.nil?
|
25
|
+
div
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
public
|
30
|
+
|
31
|
+
def initialize(out)
|
32
|
+
@outstream = out
|
33
|
+
@doc = Nokogiri::HTML::Document.new
|
34
|
+
@html = node("html", @doc)
|
35
|
+
node_head = node("head", @html)
|
36
|
+
node_style = node("style", node_head, {:type => 'text/css'})
|
37
|
+
node_style.inner_html = css
|
38
|
+
node_body = node("body", @html)
|
39
|
+
node("h1", node_body, {}, 'Nukumber test report')
|
40
|
+
end
|
41
|
+
|
42
|
+
def begin_feature(feature)
|
43
|
+
@node_feature = node('div', @html.at_css('body'), {:class => 'feature'})
|
44
|
+
node_header = node('div', @node_feature, {:class => 'header'})
|
45
|
+
node("h2", node_header, {}, "#{feature.keyword}: #{feature.name}")
|
46
|
+
unless feature.tags.empty?
|
47
|
+
node("div", node_header, {:class => 'tags'}, feature.tags.join(' '))
|
48
|
+
end
|
49
|
+
unless feature.description.empty?
|
50
|
+
node("p", node_header, {}, feature.description.gsub(/\n/,' '))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def begin_element(element)
|
55
|
+
@node_element = node("div", @node_feature, {:class => 'element'})
|
56
|
+
node_header = node('div', @node_element, {:class => 'header'})
|
57
|
+
node("h3", node_header, {}, "#{element.keyword}: #{element.name}")
|
58
|
+
node("div", node_header, {:class => 'address'}, "#{element.feature.file_path.split('/').last}:#{element.line}")
|
59
|
+
unless element.is_a? Nukumber::Model::Background or element.tags.empty?
|
60
|
+
node("div", node_header, {:class => 'tags'}, element.tags.join(' '))
|
61
|
+
end
|
62
|
+
unless element.description.empty?
|
63
|
+
node("p", node_header, {}, element.description.gsub(/\n/,' '))
|
64
|
+
end
|
65
|
+
if element.is_a? Nukumber::Model::ScenarioOutline
|
66
|
+
element.steps.each { |step| print_step(step, :outline) }
|
67
|
+
@node_ex_table = node("table", @node_element)
|
68
|
+
add_row(element.examples.table, nil, @node_ex_table)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def undefined_element(element)
|
73
|
+
@node_element = node("div", @node_feature, {:class => 'element undefined'})
|
74
|
+
node_header = node('div', @node_element, {:class => 'header'})
|
75
|
+
node("h3", node_header, {}, "#{element.keyword}: #{element.name}")
|
76
|
+
node("div", node_header, {:class => 'address'}, "#{element.feature.file_path.split('/').last}:#{element.line}")
|
77
|
+
unless element.is_a? Nukumber::Model::Background or element.tags.empty?
|
78
|
+
node("div", node_header, {:class => 'tags'}, element.tags.join(' '))
|
79
|
+
end
|
80
|
+
unless element.description.empty?
|
81
|
+
node("p", node_header, {}, element.description.gsub(/\n/,' '))
|
82
|
+
end
|
83
|
+
element.steps.each { |step| print_step(step, :undefined) }
|
84
|
+
if element.is_a? Nukumber::Model::ScenarioOutline
|
85
|
+
@node_ex_table = node("table", @node_element)
|
86
|
+
(0..element.examples.table.row_count).each do |i|
|
87
|
+
add_row(element.examples.table, i - 1, @node_ex_table, 'undefined')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def print_step(step, status)
|
93
|
+
node_step = node("div", @node_element, {:class => "step #{status.to_s}"})
|
94
|
+
node("p", node_step, {}, "#{step.keyword}#{step.name}")
|
95
|
+
unless step.args.empty?
|
96
|
+
node_table = Nokogiri::XML::Node.new "table", @doc
|
97
|
+
(0..step.args.row_count).each do |i|
|
98
|
+
add_row(step.args, i - 1, node_table)
|
99
|
+
end
|
100
|
+
node_step.add_child node_table
|
101
|
+
end
|
102
|
+
@node_element['class'] = "element #{status.to_s}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def print_example(table, row, status)
|
106
|
+
add_row(table, row, @node_ex_table, status.to_s)
|
107
|
+
unless @node_element['class'] == "element failed"
|
108
|
+
@node_element['class'] = "element #{status.to_s}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def error(exception, element)
|
113
|
+
node_error = node("div", @node_element, {:class => "error"})
|
114
|
+
node("p", node_error, {}, exception.message)
|
115
|
+
node_backtrace = node("div", node_error)
|
116
|
+
filtered_backtrace(exception.backtrace).each do |l|
|
117
|
+
node("p", node_backtrace, {}, l)
|
118
|
+
end
|
119
|
+
node("p", node_backtrace, {}, "#{element.feature.file_path}:#{element.line}")
|
120
|
+
@node_element['class'] = 'element failed'
|
121
|
+
end
|
122
|
+
|
123
|
+
def final_report(passed, failed, pending, undefined)
|
124
|
+
rpt = {:passed => passed, :failed => failed, :pending => pending, :undefined => undefined}
|
125
|
+
node_final_report = node("div", @html.at_css('body'), {:id => 'final_report'})
|
126
|
+
%w( passed failed pending undefined ).each do |str|
|
127
|
+
node("div", node_final_report, {:class => str}, "#{rpt[str.to_sym].size} test#{rpt[str.to_sym].size == 1 ? '' : 's'} #{str}")
|
128
|
+
end
|
129
|
+
node("div", node_final_report, {:class => "datetime"}, "#{Time.now}")
|
130
|
+
end
|
131
|
+
|
132
|
+
def print_skeleton_code(*)
|
133
|
+
end
|
134
|
+
|
135
|
+
def terminate()
|
136
|
+
@outstream.puts @doc.to_html
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module Nukumber
|
2
|
+
|
3
|
+
module Reporter
|
4
|
+
|
5
|
+
class Html < Abstract
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def css()
|
10
|
+
<<HERE
|
11
|
+
<!--
|
12
|
+
|
13
|
+
/* http://meyerweb.com/eric/tools/css/reset/
|
14
|
+
end
|
15
|
+
v2.0 | 20110126
|
16
|
+
License: none (public domain)
|
17
|
+
*/
|
18
|
+
|
19
|
+
html, body, div, span, applet, object, iframe,
|
20
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
21
|
+
a, abbr, acronym, address, big, cite, code,
|
22
|
+
del, dfn, em, img, ins, kbd, q, s, samp,
|
23
|
+
small, strike, strong, sub, sup, tt, var,
|
24
|
+
b, u, i, center, dl, dt, dd, ol, ul, li,
|
25
|
+
fieldset, form, label, legend, table, caption,
|
26
|
+
tbody, tfoot, thead, tr, th, td, article, aside,
|
27
|
+
canvas, details, embed, figure, figcaption,
|
28
|
+
footer, header, hgroup, menu, nav, output, ruby,
|
29
|
+
section, summary, time, mark, audio, video {
|
30
|
+
margin: 0;
|
31
|
+
padding: 0;
|
32
|
+
border: 0;
|
33
|
+
font-size: 100%;
|
34
|
+
font: inherit;
|
35
|
+
vertical-align: baseline;
|
36
|
+
}
|
37
|
+
/* HTML5 display-role reset for older browsers */
|
38
|
+
article, aside, details, figcaption, figure,
|
39
|
+
footer, header, hgroup, menu, nav, section {
|
40
|
+
display: block;
|
41
|
+
}
|
42
|
+
body {
|
43
|
+
line-height: 1;
|
44
|
+
}
|
45
|
+
ol, ul {
|
46
|
+
list-style: none;
|
47
|
+
}
|
48
|
+
blockquote, q {
|
49
|
+
quotes: none;
|
50
|
+
}
|
51
|
+
blockquote:before, blockquote:after,
|
52
|
+
q:before, q:after {
|
53
|
+
content: '';
|
54
|
+
content: none;
|
55
|
+
}
|
56
|
+
table {
|
57
|
+
border-collapse: collapse;
|
58
|
+
border-spacing: 0;
|
59
|
+
}
|
60
|
+
|
61
|
+
/* ========================== END MEYER RESET ========================== */
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
html {
|
66
|
+
font-family: monospace;
|
67
|
+
}
|
68
|
+
|
69
|
+
body {
|
70
|
+
padding: 10px;
|
71
|
+
line-height: 1.4;
|
72
|
+
}
|
73
|
+
|
74
|
+
h1 { font-size: 150%; font-weight: bold; }
|
75
|
+
h2 { font-size: 130%; font-weight: bold; }
|
76
|
+
h3 { font-size: 110%; font-weight: bold; }
|
77
|
+
|
78
|
+
table, th, td { border: 1px solid #000; padding: 3px; }
|
79
|
+
th { background-color: #FFF }
|
80
|
+
|
81
|
+
.feature {
|
82
|
+
padding: 10px;
|
83
|
+
margin-bottom: 10px;
|
84
|
+
}
|
85
|
+
|
86
|
+
.element {
|
87
|
+
width: 100%;
|
88
|
+
overflow: auto;
|
89
|
+
border: 1px dashed #000;
|
90
|
+
padding: 10px;
|
91
|
+
margin-bottom: 10px;
|
92
|
+
}
|
93
|
+
|
94
|
+
.header { margin-bottom: 1ex; min-height: 2.5ex; }
|
95
|
+
.header h3 { float:left; }
|
96
|
+
.header .address { float:right; font-size: 80%; }
|
97
|
+
|
98
|
+
.header .tags { font-weight: bold; }
|
99
|
+
|
100
|
+
.header p,
|
101
|
+
.header .tags,
|
102
|
+
.step {
|
103
|
+
clear: both;
|
104
|
+
}
|
105
|
+
|
106
|
+
.step { margin-bottom: 4px; }
|
107
|
+
|
108
|
+
.passed {
|
109
|
+
background-color: #8E6;
|
110
|
+
}
|
111
|
+
|
112
|
+
.element.passed {
|
113
|
+
background-color: #BFB;
|
114
|
+
}
|
115
|
+
|
116
|
+
.failed {
|
117
|
+
background-color: #D00;
|
118
|
+
}
|
119
|
+
|
120
|
+
.element.failed {
|
121
|
+
background-color: #FCC;
|
122
|
+
}
|
123
|
+
|
124
|
+
.pending, .outline {
|
125
|
+
background-color: #2FE;
|
126
|
+
}
|
127
|
+
|
128
|
+
.element.pending, .element.outline {
|
129
|
+
background-color: #CFF;
|
130
|
+
}
|
131
|
+
|
132
|
+
.undefined {
|
133
|
+
background-color: #FF2;
|
134
|
+
}
|
135
|
+
|
136
|
+
.error {
|
137
|
+
padding: 10px;
|
138
|
+
}
|
139
|
+
|
140
|
+
.error div {
|
141
|
+
background-color: #000;
|
142
|
+
color: #F00;
|
143
|
+
padding: 10px;
|
144
|
+
}
|
145
|
+
|
146
|
+
#final_report {
|
147
|
+
position: fixed;
|
148
|
+
top: 0;
|
149
|
+
right: 0;
|
150
|
+
border: 1px solid #000;
|
151
|
+
}
|
152
|
+
|
153
|
+
#final_report div {
|
154
|
+
padding: 6px;
|
155
|
+
}
|
156
|
+
|
157
|
+
#final_report .datetime {
|
158
|
+
background-color: #FFF;
|
159
|
+
}
|
160
|
+
|
161
|
+
-->
|
162
|
+
HERE
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|