nukumber 0.4.1
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.
- 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
|