qed 2.9.1 → 3.0.0
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 +7 -0
- data/Gemfile +3 -0
- data/HISTORY.md +48 -0
- data/README.md +75 -81
- data/demo/13_fenced_code_blocks.md +60 -0
- data/lib/qed/applique.rb +10 -4
- data/lib/qed/cli/qed.rb +10 -11
- data/lib/qed/cli.rb +4 -9
- data/lib/qed/core_ext.rb +35 -33
- data/lib/qed/demo.rb +3 -1
- data/lib/qed/document.rb +5 -6
- data/lib/qed/evaluator.rb +1 -1
- data/lib/qed/parser.rb +43 -12
- data/lib/qed/reporter/abstract.rb +2 -1
- data/lib/qed/reporter/html.rb +120 -113
- data/lib/qed/reporter/tapy.rb +2 -2
- data/lib/qed/session.rb +2 -2
- data/lib/qed/utils.rb +2 -2
- data/lib/qed/version.rb +3 -0
- data/lib/qed.rb +2 -20
- metadata +39 -106
- data/.index +0 -69
- data/.yardopts +0 -10
- data/lib/qed.yml +0 -69
data/lib/qed/parser.rb
CHANGED
|
@@ -101,30 +101,61 @@ module QED
|
|
|
101
101
|
steps = []
|
|
102
102
|
blank = false
|
|
103
103
|
indented = false
|
|
104
|
+
fenced = false # inside a ``` ruby/bare code fence
|
|
105
|
+
foreign = false # inside a ``` non-ruby code fence
|
|
104
106
|
explain = []
|
|
105
107
|
example = [] #Step.new(file)
|
|
106
108
|
|
|
107
109
|
lines.each do |lineno, line|
|
|
108
110
|
case line
|
|
109
|
-
when
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
when /\A```ruby\s*$/ # ```ruby opens a ruby fence
|
|
112
|
+
fenced = true
|
|
113
|
+
foreign = false
|
|
114
|
+
next
|
|
115
|
+
when /\A```\s*$/ # bare ``` either opens or closes a fence
|
|
116
|
+
if fenced || foreign
|
|
117
|
+
fenced = false
|
|
118
|
+
foreign = false
|
|
119
|
+
next
|
|
113
120
|
else
|
|
114
|
-
|
|
121
|
+
fenced = true # bare ``` opens a ruby fence
|
|
122
|
+
next
|
|
115
123
|
end
|
|
116
|
-
when /\A
|
|
124
|
+
when /\A```\S/ # ```<language> opens a foreign fence
|
|
125
|
+
foreign = true
|
|
126
|
+
next
|
|
127
|
+
else
|
|
128
|
+
# skip lines inside foreign code fences
|
|
129
|
+
next if foreign
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
if fenced
|
|
133
|
+
# lines inside a ruby fence are treated as example code
|
|
117
134
|
indented = true
|
|
118
135
|
blank = false
|
|
119
136
|
example << [lineno, line]
|
|
120
137
|
else
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
138
|
+
case line
|
|
139
|
+
when /^\s*$/ # blank line
|
|
140
|
+
blank = true
|
|
141
|
+
if indented
|
|
142
|
+
example << [lineno, line]
|
|
143
|
+
else
|
|
144
|
+
explain << [lineno, line]
|
|
145
|
+
end
|
|
146
|
+
when /\A\s+/ # indented
|
|
147
|
+
indented = true
|
|
148
|
+
blank = false
|
|
149
|
+
example << [lineno, line]
|
|
150
|
+
else
|
|
151
|
+
if indented or blank
|
|
152
|
+
steps << Step.new(demo, explain, example, steps.last)
|
|
153
|
+
explain, example = [], []
|
|
154
|
+
end
|
|
155
|
+
indented = false
|
|
156
|
+
blank = false
|
|
157
|
+
explain << [lineno, line]
|
|
124
158
|
end
|
|
125
|
-
indented = false
|
|
126
|
-
blank = false
|
|
127
|
-
explain << [lineno, line]
|
|
128
159
|
end
|
|
129
160
|
end
|
|
130
161
|
steps << Step.new(demo, explain, example, steps.last)
|
data/lib/qed/reporter/html.rb
CHANGED
|
@@ -1,160 +1,167 @@
|
|
|
1
|
-
|
|
1
|
+
require 'erb'
|
|
2
|
+
require 'kramdown'
|
|
3
|
+
require 'qed/reporter/abstract'
|
|
2
4
|
|
|
3
5
|
module QED
|
|
4
6
|
module Reporter
|
|
5
7
|
|
|
6
|
-
require 'qed/reporter/abstract'
|
|
7
|
-
|
|
8
8
|
# = Html Reporter
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# with a farily clever way to handle this. Take the original
|
|
14
|
-
# and use Tilt to translate it into HTML, then take the
|
|
15
|
-
# evaluation results for code steps and use it to search
|
|
16
|
-
# the HTML for "the closest match". Find the \<pre> tag
|
|
17
|
-
# associated with the text and add class and color style.
|
|
18
|
-
# Of course the tricky part is the matching, but if we
|
|
19
|
-
# run the text snippet through Tilt as well we should be
|
|
20
|
-
# able to get an exact match. It won't be fast, but it should
|
|
21
|
-
# work.
|
|
22
|
-
|
|
10
|
+
# Generates a self-contained HTML report of a QED session,
|
|
11
|
+
# with color-coded pass/fail/error results.
|
|
12
|
+
#
|
|
23
13
|
class Html < Abstract
|
|
24
14
|
|
|
25
|
-
#
|
|
26
|
-
def initialize(*args)
|
|
27
|
-
require 'erb'
|
|
28
|
-
|
|
29
|
-
begin
|
|
30
|
-
require 'rubygems'
|
|
31
|
-
gem 'rdoc'
|
|
32
|
-
require 'rdoc'
|
|
33
|
-
rescue
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
super(*args)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
#
|
|
40
15
|
def before_session(session)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<head>
|
|
44
|
-
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
|
45
|
-
<title>QED Report</title>
|
|
46
|
-
<style>
|
|
47
|
-
body{width:800px; margin:0 auto;}
|
|
48
|
-
pre{font-family: courier,monospace;}
|
|
49
|
-
.pass{color: #020;}
|
|
50
|
-
.pass pre{color: green;}
|
|
51
|
-
.fail{color: #200; background: pink;}
|
|
52
|
-
.fail pre{color: green;}
|
|
53
|
-
.error{color: #200; background: pink;}
|
|
54
|
-
.error pre{color: red;}
|
|
55
|
-
</style>
|
|
56
|
-
</head>
|
|
57
|
-
<body>
|
|
58
|
-
END
|
|
16
|
+
super(session)
|
|
17
|
+
io.puts HTML_HEAD
|
|
59
18
|
end
|
|
60
19
|
|
|
61
|
-
#
|
|
62
20
|
def before_demo(demo)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
21
|
+
super(demo)
|
|
22
|
+
io.puts %[<div class="demo">]
|
|
23
|
+
io.puts %[<h2 class="demo-file">#{escape(localize_file(demo.file))}</h2>]
|
|
66
24
|
end
|
|
67
25
|
|
|
68
26
|
def step(step)
|
|
69
27
|
@_explain = step.explain.dup
|
|
70
28
|
end
|
|
71
29
|
|
|
72
|
-
#
|
|
73
30
|
def match(step, md)
|
|
74
|
-
#@match = md
|
|
75
31
|
unless md[0].empty?
|
|
76
|
-
@_explain.sub!(md[0], "<
|
|
32
|
+
@_explain.sub!(md[0], "<mark>#{escape(md[0])}</mark>")
|
|
77
33
|
end
|
|
78
34
|
end
|
|
79
35
|
|
|
80
|
-
#
|
|
81
36
|
def pass(step)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
37
|
+
super(step)
|
|
38
|
+
io.puts %[<div class="step pass">]
|
|
39
|
+
io.puts render(@_explain)
|
|
40
|
+
if step.has_example?
|
|
41
|
+
io.puts %[<pre class="code pass">#{escape(step.example)}</pre>]
|
|
42
|
+
end
|
|
43
|
+
io.puts %[</div>]
|
|
89
44
|
end
|
|
90
45
|
|
|
91
|
-
#
|
|
92
46
|
def fail(step, assertion)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<% end %>
|
|
105
|
-
</ol>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
END
|
|
47
|
+
super(step, assertion)
|
|
48
|
+
io.puts %[<div class="step fail">]
|
|
49
|
+
io.puts render(@_explain)
|
|
50
|
+
if step.has_example?
|
|
51
|
+
io.puts %[<pre class="code fail">#{escape(step.example)}</pre>]
|
|
52
|
+
end
|
|
53
|
+
io.puts %[<div class="details">]
|
|
54
|
+
io.puts %[<p class="message">FAIL: #{escape(assertion.message)}</p>]
|
|
55
|
+
io.puts %[<pre class="backtrace">#{escape(sane_backtrace(assertion).join("\n"))}</pre>]
|
|
56
|
+
io.puts %[</div>]
|
|
57
|
+
io.puts %[</div>]
|
|
109
58
|
end
|
|
110
59
|
|
|
111
|
-
#
|
|
112
60
|
def error(step, exception)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<% end %>
|
|
125
|
-
</ol>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
END
|
|
61
|
+
super(step, exception)
|
|
62
|
+
io.puts %[<div class="step error">]
|
|
63
|
+
io.puts render(@_explain)
|
|
64
|
+
if step.has_example?
|
|
65
|
+
io.puts %[<pre class="code error">#{escape(step.example)}</pre>]
|
|
66
|
+
end
|
|
67
|
+
io.puts %[<div class="details">]
|
|
68
|
+
io.puts %[<p class="message">ERROR: #{escape(exception.class.to_s)} - #{escape(exception.message)}</p>]
|
|
69
|
+
io.puts %[<pre class="backtrace">#{escape(sane_backtrace(exception).join("\n"))}</pre>]
|
|
70
|
+
io.puts %[</div>]
|
|
71
|
+
io.puts %[</div>]
|
|
129
72
|
end
|
|
130
73
|
|
|
131
|
-
#
|
|
132
74
|
def after_demo(demo)
|
|
75
|
+
super(demo)
|
|
76
|
+
io.puts %[</div>]
|
|
133
77
|
end
|
|
134
78
|
|
|
135
|
-
#
|
|
136
79
|
def after_session(session)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
80
|
+
super(session)
|
|
81
|
+
|
|
82
|
+
pass_count = passes.size
|
|
83
|
+
fail_count = fails.size
|
|
84
|
+
error_count = errors.size
|
|
85
|
+
total = steps.size
|
|
86
|
+
|
|
87
|
+
status = (fail_count + error_count) == 0 ? 'pass' : 'fail'
|
|
88
|
+
|
|
89
|
+
io.puts %[<div class="summary #{status}">]
|
|
90
|
+
io.puts %[<p>#{demos.size} demos, #{total} steps: #{fail_count} failures, #{error_count} errors</p>]
|
|
91
|
+
io.puts %[<p class="time">Finished in %.5f seconds</p>] % [Time.now - @start_time]
|
|
92
|
+
io.puts %[</div>]
|
|
93
|
+
io.puts HTML_FOOT
|
|
141
94
|
end
|
|
142
95
|
|
|
143
96
|
private
|
|
144
97
|
|
|
145
98
|
def render(str)
|
|
146
|
-
|
|
99
|
+
Kramdown::Document.new(str.strip).to_html
|
|
147
100
|
end
|
|
148
101
|
|
|
149
|
-
def
|
|
150
|
-
|
|
102
|
+
def escape(str)
|
|
103
|
+
ERB::Util.html_escape(str.to_s)
|
|
151
104
|
end
|
|
152
105
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
106
|
+
HTML_HEAD = <<~'HTML'
|
|
107
|
+
<!DOCTYPE html>
|
|
108
|
+
<html>
|
|
109
|
+
<head>
|
|
110
|
+
<meta charset="utf-8">
|
|
111
|
+
<title>QED Report</title>
|
|
112
|
+
<style>
|
|
113
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
114
|
+
body {
|
|
115
|
+
max-width: 860px; margin: 2em auto; padding: 0 1em;
|
|
116
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
|
117
|
+
font-size: 15px; line-height: 1.6; color: #24292e; background: #fff;
|
|
118
|
+
}
|
|
119
|
+
h1 { font-size: 1.6em; margin: 0 0 1em; padding-bottom: 0.3em; border-bottom: 1px solid #eaecef; }
|
|
120
|
+
h2.demo-file {
|
|
121
|
+
font-size: 1.1em; margin: 1.5em 0 0.5em; padding: 0.4em 0.6em;
|
|
122
|
+
background: #f1f3f5; border-radius: 4px; font-family: monospace;
|
|
123
|
+
}
|
|
124
|
+
.step { margin: 0.5em 0; padding: 0.6em 0.8em; border-left: 3px solid #ddd; }
|
|
125
|
+
.step.pass { border-left-color: #28a745; }
|
|
126
|
+
.step.fail { border-left-color: #d73a49; background: #ffeef0; }
|
|
127
|
+
.step.error { border-left-color: #b31d28; background: #ffeef0; }
|
|
128
|
+
.step p { margin: 0.3em 0; }
|
|
129
|
+
.step h1, .step h2, .step h3 { margin: 0.3em 0; font-size: 1.1em; }
|
|
130
|
+
pre.code {
|
|
131
|
+
margin: 0.4em 0; padding: 0.6em 0.8em;
|
|
132
|
+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
133
|
+
font-size: 13px; line-height: 1.45;
|
|
134
|
+
background: #f6f8fa; border-radius: 4px; overflow-x: auto;
|
|
135
|
+
}
|
|
136
|
+
pre.code.pass { background: #f0fff0; color: #22863a; }
|
|
137
|
+
pre.code.fail { background: #fff0f0; color: #b31d28; }
|
|
138
|
+
pre.code.error { background: #fff0f0; color: #b31d28; }
|
|
139
|
+
.details { margin: 0.4em 0; }
|
|
140
|
+
.details .message { font-weight: 600; color: #d73a49; margin: 0.3em 0; }
|
|
141
|
+
.details .backtrace {
|
|
142
|
+
font-size: 12px; color: #6a737d; background: #fafbfc;
|
|
143
|
+
padding: 0.5em; border-radius: 4px; white-space: pre-wrap;
|
|
144
|
+
}
|
|
145
|
+
.summary {
|
|
146
|
+
margin: 2em 0 1em; padding: 0.8em 1em;
|
|
147
|
+
border-radius: 4px; font-weight: 600;
|
|
148
|
+
}
|
|
149
|
+
.summary.pass { background: #dcffe4; color: #165c26; }
|
|
150
|
+
.summary.fail { background: #ffeef0; color: #b31d28; }
|
|
151
|
+
.summary .time { font-weight: normal; font-size: 0.9em; color: #586069; }
|
|
152
|
+
mark { background: #fff3cd; padding: 0 2px; border-radius: 2px; }
|
|
153
|
+
</style>
|
|
154
|
+
</head>
|
|
155
|
+
<body>
|
|
156
|
+
<h1>QED Report</h1>
|
|
157
|
+
HTML
|
|
158
|
+
|
|
159
|
+
HTML_FOOT = <<~'HTML'
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
|
162
|
+
HTML
|
|
157
163
|
|
|
158
|
-
end
|
|
159
|
-
end#module QED
|
|
164
|
+
end
|
|
160
165
|
|
|
166
|
+
end
|
|
167
|
+
end
|
data/lib/qed/reporter/tapy.rb
CHANGED
|
@@ -148,8 +148,8 @@ module Reporter #:nodoc:
|
|
|
148
148
|
#'expected' => nil,
|
|
149
149
|
'time' => time_since_start,
|
|
150
150
|
'exception' => {
|
|
151
|
-
'message' =>
|
|
152
|
-
'class' =>
|
|
151
|
+
'message' => exception.message, #unansi
|
|
152
|
+
'class' => exception.class.name,
|
|
153
153
|
'file' => file,
|
|
154
154
|
'line' => line,
|
|
155
155
|
'source' => source,
|
data/lib/qed/session.rb
CHANGED
|
@@ -84,7 +84,7 @@ module QED
|
|
|
84
84
|
|
|
85
85
|
#
|
|
86
86
|
def directory
|
|
87
|
-
settings.tmpdir
|
|
87
|
+
rooted ? settings.root_directory : settings.tmpdir
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# Top-level configuration.
|
|
@@ -160,7 +160,7 @@ module QED
|
|
|
160
160
|
|
|
161
161
|
# Clear temporary testing directory.
|
|
162
162
|
def clear_directory
|
|
163
|
-
settings.clear_directory
|
|
163
|
+
settings.clear_directory unless rooted
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
# Set $ASSERTION_COUNTS to zero point.
|
data/lib/qed/utils.rb
CHANGED
|
@@ -4,7 +4,7 @@ module QED
|
|
|
4
4
|
ROOT_PATTERN = '{.ruby*,.git/,.hg/,_darcs/}'
|
|
5
5
|
|
|
6
6
|
# Glob pattern for standard config file.
|
|
7
|
-
CONFIG_PATTERN = '{etc/qed.rb,config/qed.rb,Qedfile
|
|
7
|
+
CONFIG_PATTERN = '{.qed,etc/qed.rb,config/qed.rb,Qedfile}'
|
|
8
8
|
|
|
9
9
|
# Home directory.
|
|
10
10
|
HOME = File.expand_path('~')
|
|
@@ -21,7 +21,7 @@ module QED
|
|
|
21
21
|
|
|
22
22
|
#
|
|
23
23
|
def load_rc
|
|
24
|
-
rc_file= File.join(root, '.rubyrc')
|
|
24
|
+
rc_file = File.join(root, '.rubyrc')
|
|
25
25
|
if File.exist?(rc_file)
|
|
26
26
|
begin
|
|
27
27
|
require 'rc/api'
|
data/lib/qed/version.rb
ADDED
data/lib/qed.rb
CHANGED
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'qed/version'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
def self.metadata
|
|
5
|
-
@metadata ||= (
|
|
6
|
-
require 'yaml'
|
|
7
|
-
YAML.load(File.new(File.dirname(__FILE__) + '/qed.yml')) rescue {}
|
|
8
|
-
)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Access to project metadata as constants.
|
|
12
|
-
def self.const_missing(name)
|
|
13
|
-
key = name.to_s.downcase
|
|
14
|
-
metadata[key] || super(name)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# TODO: Only b/c of Ruby 1.8.x bug.
|
|
18
|
-
VERSION = metadata['version']
|
|
19
|
-
|
|
20
|
-
end
|
|
3
|
+
module QED; end
|
|
21
4
|
|
|
22
5
|
require 'qed/session'
|
|
23
6
|
require 'qed/document'
|
|
24
|
-
|