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.
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 /^\s*$/ # blank line
110
- blank = true
111
- if indented
112
- example << [lineno, line]
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
- explain << [lineno, line]
121
+ fenced = true # bare ``` opens a ruby fence
122
+ next
115
123
  end
116
- when /\A\s+/ #/\A(\t|\ \ +)/ # indented
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
- if indented or blank
122
- steps << Step.new(demo, explain, example, steps.last)
123
- explain, example = [], [] #Step.new(file)
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)
@@ -1,7 +1,8 @@
1
1
  module QED
2
2
  module Reporter
3
3
 
4
- require 'facets/string'
4
+ # TODO: What was this for?
5
+ #require 'facets/string'
5
6
 
6
7
  begin
7
8
  require 'ansi/core'
@@ -1,160 +1,167 @@
1
- # encoding: UTF-8
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
- # NOTE: This must be completely redesigned since we moved back
11
- # to text based evaluation --which makes generting HTML with
12
- # modifications from the evaluation tricky. But I've come up
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
- io.puts <<-END
42
- <html>
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
- io.puts <<-END
64
- <h2>#{localize_file(demo.file)}</h2>
65
- END
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], "<b>#{md[0]}</b>")
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
- io.puts <<-END
83
- <div class="test pass">
84
- #{render(@_explain)}
85
-
86
- <pre>#{step.example}</pre>
87
- </div>
88
- END
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
- io.puts ERB.new(<<-END).result(binding)
94
- <div class="test fail">
95
- #{render(@_explain)}
96
-
97
- <pre>#{step.example}</pre>
98
-
99
- <div class="assertion">
100
- <p>#{assertion.class} - #{assertion.message}</p>
101
- <ol>
102
- <% assertion.backtrace.each do |bt| %>
103
- <li><%= bt %></li>
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
- io.puts ERB.new(<<-END).result(binding)
114
- <div class="test error">
115
- #{render(@_explain)}
116
-
117
- <pre>#{step.example}</pre>
118
-
119
- <div class="exception">
120
- <p>#{exception.class} - #{exception.message}</p>
121
- <ol>
122
- <% exception.backtrace.each do |bt| %>
123
- <li><%= bt %></li>
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
- io.puts <<-END
138
- </body>
139
- </html>
140
- END
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
- rdoc.convert(str.strip)
99
+ Kramdown::Document.new(str.strip).to_html
147
100
  end
148
101
 
149
- def rdoc
150
- @rdoc ||= RDoc::Markup::ToHtml.new
102
+ def escape(str)
103
+ ERB::Util.html_escape(str.to_s)
151
104
  end
152
105
 
153
- #def h(str)
154
- # ERB::Util.html_escape(str)
155
- #end
156
- end
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#module Reporter
159
- end#module QED
164
+ end
160
165
 
166
+ end
167
+ end
@@ -148,8 +148,8 @@ module Reporter #:nodoc:
148
148
  #'expected' => nil,
149
149
  'time' => time_since_start,
150
150
  'exception' => {
151
- 'message' => assertion.message, #unansi
152
- 'class' => assertion.class.name,
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,.qed}'
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'
@@ -0,0 +1,3 @@
1
+ module QED
2
+ VERSION = '3.0.0'
3
+ end
data/lib/qed.rb CHANGED
@@ -1,24 +1,6 @@
1
- module QED
1
+ require_relative 'qed/version'
2
2
 
3
- # Access to project metadata.
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
-