teaspoon 1.1.3 → 1.2.2
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 +5 -5
- data/CHANGELOG.md +47 -0
- data/MIT.LICENSE +2 -2
- data/README.md +56 -35
- data/app/assets/javascripts/teaspoon/hook.coffee +1 -1
- data/app/assets/javascripts/teaspoon/teaspoon.coffee +13 -1
- data/app/controllers/teaspoon/suite_controller.rb +18 -13
- data/app/views/teaspoon/suite/_boot.html.erb +1 -1
- data/app/views/teaspoon/suite/index.html.erb +1 -1
- data/lib/generators/teaspoon/install/install_generator.rb +34 -34
- data/lib/generators/teaspoon/install/templates/MISSING_FRAMEWORK +1 -1
- data/lib/generators/teaspoon/install/templates/POST_INSTALL +1 -1
- data/lib/generators/teaspoon/install/templates/env_comments.rb.tt +9 -7
- data/lib/tasks/teaspoon/info.rake +1 -1
- data/lib/teaspoon/command_line.rb +76 -76
- data/lib/teaspoon/configuration.rb +4 -4
- data/lib/teaspoon/console.rb +41 -40
- data/lib/teaspoon/coverage.rb +29 -25
- data/lib/teaspoon/deprecated.rb +1 -1
- data/lib/teaspoon/driver.rb +1 -1
- data/lib/teaspoon/driver/browserstack.rb +111 -0
- data/lib/teaspoon/driver/phantomjs.rb +25 -26
- data/lib/teaspoon/driver/phantomjs/runner.js +31 -3
- data/lib/teaspoon/driver/selenium.rb +13 -9
- data/lib/teaspoon/engine.rb +32 -27
- data/lib/teaspoon/environment.rb +28 -28
- data/lib/teaspoon/exceptions.rb +7 -7
- data/lib/teaspoon/exporter.rb +23 -23
- data/lib/teaspoon/formatter/base.rb +64 -46
- data/lib/teaspoon/formatter/clean.rb +2 -2
- data/lib/teaspoon/formatter/documentation.rb +40 -40
- data/lib/teaspoon/formatter/dot.rb +12 -12
- data/lib/teaspoon/formatter/json.rb +21 -21
- data/lib/teaspoon/formatter/junit.rb +52 -51
- data/lib/teaspoon/formatter/modules/report_module.rb +34 -32
- data/lib/teaspoon/formatter/pride.rb +21 -21
- data/lib/teaspoon/formatter/rspec_html.rb +31 -31
- data/lib/teaspoon/formatter/snowday.rb +6 -5
- data/lib/teaspoon/formatter/swayze_or_oprah.rb +91 -91
- data/lib/teaspoon/formatter/tap.rb +29 -29
- data/lib/teaspoon/formatter/tap_y.rb +57 -57
- data/lib/teaspoon/formatter/teamcity.rb +63 -63
- data/lib/teaspoon/framework/base.rb +1 -1
- data/lib/teaspoon/instrumentation.rb +18 -18
- data/lib/teaspoon/registry.rb +9 -9
- data/lib/teaspoon/registry/has_default.rb +2 -2
- data/lib/teaspoon/runner.rb +37 -37
- data/lib/teaspoon/server.rb +30 -29
- data/lib/teaspoon/suite.rb +68 -57
- data/lib/teaspoon/utility.rb +6 -0
- data/lib/teaspoon/version.rb +1 -1
- metadata +22 -15
- data/lib/teaspoon/driver/capybara_webkit.rb +0 -40
data/lib/teaspoon/exporter.rb
CHANGED
@@ -20,33 +20,33 @@ module Teaspoon
|
|
20
20
|
|
21
21
|
private
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def executable
|
24
|
+
return @executable if @executable
|
25
|
+
@executable = which("wget")
|
26
|
+
return @executable unless @executable.blank?
|
27
|
+
raise Teaspoon::MissingDependencyError.new("Unable to locate `wget` for exporter.")
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
def create_export(path)
|
31
|
+
Dir.chdir(path) do
|
32
|
+
update_relative_paths
|
33
|
+
cleanup_output
|
34
|
+
move_output
|
35
|
+
end
|
35
36
|
end
|
36
|
-
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
def update_relative_paths
|
39
|
+
html = File.read(".#{Teaspoon.configuration.mount_at}/#{@suite}.html")
|
40
|
+
File.write("index.html", html.gsub!('"../', '"'))
|
41
|
+
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
def cleanup_output
|
44
|
+
FileUtils.rm_r(Dir["{.#{Teaspoon.configuration.mount_at},robots.txt.html}"])
|
45
|
+
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
def move_output
|
48
|
+
FileUtils.mkdir_p(@output_path)
|
49
|
+
FileUtils.mv(Dir["*"], @output_path, force: true)
|
50
|
+
end
|
51
51
|
end
|
52
52
|
end
|
@@ -7,7 +7,7 @@ module Teaspoon
|
|
7
7
|
|
8
8
|
def initialize(suite_name = :default, output_file = nil)
|
9
9
|
@suite_name = suite_name.to_s
|
10
|
-
@output_file = output_file
|
10
|
+
@output_file = parse_output_file(output_file)
|
11
11
|
@stdout = ""
|
12
12
|
@suite = nil
|
13
13
|
@last_suite = nil
|
@@ -86,74 +86,92 @@ module Teaspoon
|
|
86
86
|
|
87
87
|
protected
|
88
88
|
|
89
|
-
|
89
|
+
def log_runner(_result); end
|
90
90
|
|
91
|
-
|
91
|
+
def log_suite(_result); end
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
def log_spec(result)
|
94
|
+
return log_passing_spec(result) if result.passing?
|
95
|
+
return log_pending_spec(result) if result.pending?
|
96
|
+
log_failing_spec(result)
|
97
|
+
end
|
98
98
|
|
99
|
-
|
99
|
+
def log_passing_spec(_result); end
|
100
100
|
|
101
|
-
|
101
|
+
def log_pending_spec(_result); end
|
102
102
|
|
103
|
-
|
103
|
+
def log_failing_spec(_result); end
|
104
104
|
|
105
|
-
|
105
|
+
def log_error(_result); end
|
106
106
|
|
107
|
-
|
107
|
+
def log_exception(_result); end
|
108
108
|
|
109
|
-
|
109
|
+
def log_console(_message); end
|
110
110
|
|
111
|
-
|
111
|
+
def log_result(_result); end
|
112
112
|
|
113
|
-
|
113
|
+
def log_coverage(_message); end
|
114
114
|
|
115
|
-
|
115
|
+
def log_threshold_failure(_message); end
|
116
116
|
|
117
|
-
|
117
|
+
def log_complete(_failure_count); end
|
118
118
|
|
119
119
|
private
|
120
120
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
def log_str(str, color_code = nil)
|
122
|
+
return log_to_file(str, @output_file) if @output_file
|
123
|
+
STDOUT.print(color_code ? colorize(str, color_code) : str)
|
124
|
+
end
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
def log_line(str = "", color_code = nil)
|
127
|
+
return log_to_file("#{str}\n", @output_file) if @output_file
|
128
|
+
STDOUT.print("#{color_code ? colorize(str, color_code) : str}\n")
|
129
|
+
end
|
130
130
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
131
|
+
def log_to_file(str, output_file)
|
132
|
+
@_output_file = File.open(output_file, "a") { |f| f.write(str) }
|
133
|
+
rescue IOError => e
|
134
|
+
raise Teaspoon::FileWriteError.new(e.message)
|
135
|
+
end
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
def colorize(str, color_code)
|
138
|
+
return str unless Teaspoon.configuration.color || @output_file
|
139
|
+
"\e[#{color_code}m#{str}\e[0m"
|
140
|
+
end
|
141
141
|
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
def pluralize(str, value)
|
143
|
+
value == 1 ? "#{value} #{str}" : "#{value} #{str}s"
|
144
|
+
end
|
145
|
+
|
146
|
+
def filename(file)
|
147
|
+
uri = URI(file)
|
145
148
|
|
146
|
-
|
147
|
-
|
149
|
+
params = String(uri.query).split("&").reject do |param|
|
150
|
+
RESERVED_PARAMS.include?(param.split("=").first)
|
151
|
+
end
|
148
152
|
|
149
|
-
|
150
|
-
|
153
|
+
filename = uri.path.sub(%r(^/assets/), "")
|
154
|
+
filename += "?#{params.join("&")}" if params.any?
|
155
|
+
filename
|
151
156
|
end
|
152
157
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
158
|
+
def parse_output_file(output)
|
159
|
+
return output unless output
|
160
|
+
output.gsub(/%{([^}]*)}/) { parse_output_capture($1) }
|
161
|
+
end
|
162
|
+
|
163
|
+
def parse_output_capture(cap)
|
164
|
+
case cap
|
165
|
+
when "suite_name"
|
166
|
+
@suite_name
|
167
|
+
when "date"
|
168
|
+
Time.now.to_i
|
169
|
+
else
|
170
|
+
warn ["Teaspoon::Formatter - Output File can only contain the placeholders %{suite_name} or %{date}.",
|
171
|
+
"%{#{cap}} is not supported and will be ignored."].join("\n")
|
172
|
+
""
|
173
|
+
end
|
174
|
+
end
|
157
175
|
end
|
158
176
|
end
|
159
177
|
end
|
@@ -8,54 +8,54 @@ module Teaspoon
|
|
8
8
|
|
9
9
|
protected
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def initialize(*args)
|
12
|
+
@level = 0
|
13
|
+
super
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def log_suite(result)
|
17
|
+
log_indent_line(result.label, result.level)
|
18
|
+
@level = result.level
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
def log_passing_spec(result)
|
22
|
+
log_indent_spec(result.label, GREEN)
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
def log_pending_spec(result)
|
26
|
+
log_indent_spec("#{result.label} (PENDING)", YELLOW)
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def log_failing_spec(result)
|
30
|
+
log_indent_spec("#{result.label} (FAILED - #{@failures.length})", RED)
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
def log_result(result)
|
34
|
+
log_line
|
35
|
+
super
|
36
|
+
end
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
40
|
+
def log_indent_spec(str, color)
|
41
|
+
log_indent_line(str, level = (@last_suite ? @level + 1 : 0), color)
|
42
|
+
log_intent_stdout(level + 1)
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_intent_stdout(level)
|
46
|
+
return if @stdout.blank?
|
47
|
+
log_indent_line("# #{@stdout.gsub(/\n$/, '').gsub("\n", "\n# ")}", level, CYAN)
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_indent_line(str = "", level = nil, color = nil)
|
51
|
+
log_line(indent(str, level || @level), color)
|
52
|
+
end
|
53
|
+
|
54
|
+
def indent(str = "", level = nil)
|
55
|
+
indent = " " * (level * 2)
|
56
|
+
str.gsub!("\n", "\n#{indent}")
|
57
|
+
"#{indent}#{str}"
|
58
|
+
end
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -8,20 +8,20 @@ module Teaspoon
|
|
8
8
|
|
9
9
|
protected
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def log_spec(result)
|
12
|
+
return log_str(".", GREEN) if result.passing?
|
13
|
+
return log_str("*", YELLOW) if result.pending?
|
14
|
+
log_str("F", RED)
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def log_console(message)
|
18
|
+
log_str(message)
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def log_result(result)
|
22
|
+
log_line("\n")
|
23
|
+
super
|
24
|
+
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -5,33 +5,33 @@ module Teaspoon
|
|
5
5
|
class Json < Base
|
6
6
|
protected
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
def log_runner(result)
|
9
|
+
log_result(result)
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def log_suite(result)
|
13
|
+
log_result(result)
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def log_spec(result)
|
17
|
+
log_result(result)
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
def log_error(result)
|
21
|
+
log_result(result)
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
def log_exception(result)
|
25
|
+
log_result(result)
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def log_console(message)
|
29
|
+
log_line(%{{"type":"console","log":"#{message.gsub(/\n$/, '').gsub('\n', '\\n')}"}})
|
30
|
+
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
def log_result(result)
|
33
|
+
log_str(result.original_json)
|
34
|
+
end
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -6,74 +6,75 @@ module Teaspoon
|
|
6
6
|
class Junit < Base
|
7
7
|
protected
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def log_runner(result)
|
10
|
+
log_line(%{<?xml version="1.0" encoding="UTF-8"?>})
|
11
|
+
log_line(%{<testsuites name="Teaspoon">})
|
12
|
+
start_time = Time.parse(result.start).iso8601
|
13
|
+
log_line(%{<testsuite name="#{escape(@suite_name)}" tests="#{@total_count}" timestamp="#{start_time}">})
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def log_suite(result)
|
17
|
+
log_end_suite
|
18
|
+
log_line(%{<testsuite name="#{escape(result.label)}">})
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def log_passing_spec(result)
|
22
|
+
log_junit_spec(suite: result.suite, label: result.label)
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def log_pending_spec(result)
|
26
|
+
log_junit_spec(suite: result.suite, label: result.label) do
|
27
|
+
log_line(%{ <skipped/>})
|
28
|
+
end
|
27
29
|
end
|
28
|
-
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
def log_failing_spec(result)
|
32
|
+
log_junit_spec(suite: result.suite, label: result.label) do
|
33
|
+
log_line(%{ <failure type="AssertionFailed">#{cdata(result.message)}</failure>})
|
34
|
+
end
|
33
35
|
end
|
34
|
-
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def log_result(_result)
|
38
|
+
log_end_suite
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
def log_coverage(message)
|
42
|
+
properties = "<properties>#{cdata(message)}</properties>"
|
43
|
+
log_line(%{<testsuite name="Coverage summary" tests="0">\n#{properties}\n</testsuite>})
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def log_threshold_failure(message)
|
47
|
+
log_line(%{<testsuite name="Coverage thresholds" tests="1">})
|
48
|
+
log_junit_spec(suite: "Coverage thresholds", label: "were not met") do
|
49
|
+
log_line(%{ <failure type="AssertionFailed">#{cdata(message)}</failure>})
|
50
|
+
end
|
51
|
+
log_line(%{</testsuite>})
|
49
52
|
end
|
50
|
-
log_line(%{</testsuite>})
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
def log_complete(_failure_count)
|
55
|
+
log_line(%{</testsuite>\n</testsuites>})
|
56
|
+
end
|
56
57
|
|
57
58
|
private
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
def log_end_suite
|
61
|
+
log_line(%{</testsuite>}) if @last_suite
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
def log_junit_spec(opts, &_block)
|
65
|
+
log_line(%{<testcase classname="#{escape(opts[:suite])}" name="#{escape(opts[:label])}">})
|
66
|
+
yield if block_given?
|
67
|
+
log_line(%{<system-out>#{cdata(@stdout)}</system-out>}) unless @stdout.blank?
|
68
|
+
log_line(%{</testcase>})
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
def escape(str)
|
72
|
+
CGI.escapeHTML(str)
|
73
|
+
end
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
def cdata(str)
|
76
|
+
"\n<![CDATA[\n#{str.gsub(/\n$/, '')}\n]]>\n"
|
77
|
+
end
|
77
78
|
end
|
78
79
|
end
|
79
80
|
end
|