scarpe-components 0.3.0 → 0.4.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -1
  3. data/Gemfile.lock +2 -3
  4. data/README.md +2 -2
  5. data/assets/bootstrap-themes/bootstrap-cerulean.css +12229 -0
  6. data/assets/bootstrap-themes/bootstrap-cosmo.css +11810 -0
  7. data/assets/bootstrap-themes/bootstrap-cyborg.css +12210 -0
  8. data/assets/bootstrap-themes/bootstrap-darkly.css +12153 -0
  9. data/assets/bootstrap-themes/bootstrap-flatly.css +12126 -0
  10. data/assets/bootstrap-themes/bootstrap-icons.min.css +5 -0
  11. data/assets/bootstrap-themes/bootstrap-journal.css +12099 -0
  12. data/assets/bootstrap-themes/bootstrap-litera.css +12211 -0
  13. data/assets/bootstrap-themes/bootstrap-lumen.css +12369 -0
  14. data/assets/bootstrap-themes/bootstrap-lux.css +11928 -0
  15. data/assets/bootstrap-themes/bootstrap-materia.css +13184 -0
  16. data/assets/bootstrap-themes/bootstrap-minty.css +12177 -0
  17. data/assets/bootstrap-themes/bootstrap-morph.css +12750 -0
  18. data/assets/bootstrap-themes/bootstrap-pulse.css +11890 -0
  19. data/assets/bootstrap-themes/bootstrap-quartz.css +12622 -0
  20. data/assets/bootstrap-themes/bootstrap-sandstone.css +12201 -0
  21. data/assets/bootstrap-themes/bootstrap-simplex.css +12186 -0
  22. data/assets/bootstrap-themes/bootstrap-sketchy.css +12451 -0
  23. data/assets/bootstrap-themes/bootstrap-slate.css +12492 -0
  24. data/assets/bootstrap-themes/bootstrap-solar.css +12149 -0
  25. data/assets/bootstrap-themes/bootstrap-spacelab.css +12266 -0
  26. data/assets/bootstrap-themes/bootstrap-superhero.css +12216 -0
  27. data/assets/bootstrap-themes/bootstrap-united.css +12077 -0
  28. data/assets/bootstrap-themes/bootstrap-vapor.css +12549 -0
  29. data/assets/bootstrap-themes/bootstrap-yeti.css +12325 -0
  30. data/assets/bootstrap-themes/bootstrap-zephyr.css +12283 -0
  31. data/assets/bootstrap-themes/bootstrap.bundle.min.js +7 -0
  32. data/lib/scarpe/components/asset_server.rb +219 -0
  33. data/lib/scarpe/components/base64.rb +22 -0
  34. data/lib/scarpe/components/calzini/{art_widgets.rb → art_drawables.rb} +42 -18
  35. data/lib/scarpe/components/calzini/border.rb +38 -0
  36. data/lib/scarpe/components/calzini/button.rb +6 -8
  37. data/lib/scarpe/components/calzini/misc.rb +7 -17
  38. data/lib/scarpe/components/calzini/para.rb +213 -11
  39. data/lib/scarpe/components/calzini/slots.rb +14 -60
  40. data/lib/scarpe/components/calzini.rb +88 -1
  41. data/lib/scarpe/components/errors.rb +4 -0
  42. data/lib/scarpe/components/html.rb +4 -1
  43. data/lib/scarpe/components/minitest_export_reporter.rb +11 -3
  44. data/lib/scarpe/components/minitest_result.rb +41 -0
  45. data/lib/scarpe/components/print_logger.rb +17 -2
  46. data/lib/scarpe/components/process_helpers.rb +37 -0
  47. data/lib/scarpe/components/segmented_file_loader.rb +1 -1
  48. data/lib/scarpe/components/tiranti.rb +42 -100
  49. data/lib/scarpe/components/unit_test_helpers.rb +3 -1
  50. data/lib/scarpe/components/version.rb +1 -1
  51. metadata +34 -6
  52. data/lib/scarpe/components/calzini/text_widgets.rb +0 -65
@@ -16,20 +16,20 @@ module Scarpe::Components::Calzini
16
16
  oninput = handler_js_code("change", "this.value")
17
17
 
18
18
  HTML.render do |h|
19
- h.textarea(id: html_id, oninput: oninput, style: edit_box_style(props)) { props["text"] }
19
+ h.textarea(id: html_id, oninput: oninput,onmouseover: handler_js_code("hover"), style: edit_box_style(props),title: props["tooltip"]) { props["text"] }
20
20
  end
21
21
  end
22
22
 
23
23
  def edit_line_element(props)
24
24
  oninput = handler_js_code("change", "this.value")
25
-
25
+
26
26
  HTML.render do |h|
27
- h.input(id: html_id, oninput: oninput, value: props["text"], style: edit_line_style(props))
27
+ h.input(id: html_id, oninput: oninput, onmouseover: handler_js_code("hover"), value: props["text"], style: edit_line_style(props),title: props["tooltip"])
28
28
  end
29
29
  end
30
30
 
31
31
  def image_element(props)
32
- style = image_style(props)
32
+ style = drawable_style(props)
33
33
 
34
34
  if props["click"]
35
35
  HTML.render do |h|
@@ -111,26 +111,16 @@ module Scarpe::Components::Calzini
111
111
  drawable_style(props).merge({
112
112
  height: dimensions_length(props["height"]),
113
113
  width: dimensions_length(props["width"]),
114
+ font: props["font"]? parse_font(props) : nil
114
115
  }.compact)
115
116
  end
116
117
 
117
118
  def edit_line_style(props)
118
119
  styles = drawable_style(props)
119
120
 
121
+ styles[:font] = props["font"]? parse_font(props) : nil
120
122
  styles[:width] = dimensions_length(props["width"]) if props["width"]
121
-
122
- styles
123
- end
124
-
125
- def image_style(props)
126
- styles = drawable_style(props)
127
-
128
- styles[:width] = dimensions_length(props["width"]) if props["width"]
129
- styles[:height] = dimensions_length(props["height"]) if props["height"]
130
-
131
- styles[:top] = dimensions_length(props["top"]) if props["top"]
132
- styles[:left] = dimensions_length(props["left"]) if props["left"]
133
- styles[:position] = "absolute" if props["top"] || props["left"]
123
+ styles[:color] = rgb_to_hex(props["stroke"])
134
124
 
135
125
  styles
136
126
  end
@@ -1,27 +1,166 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Scarpe::Components::Calzini
4
- # para_element is a bit of a hard one, since it does not-entirely-trivial
5
- # mapping between display objects and IDs. But we don't want Calzini
6
- # messing with the display service or display objects.
7
4
  def para_element(props, &block)
5
+ # Align requires an extra wrapping div.
6
+
7
+ # Stacking strikethrough with underline requires multiple elements.
8
+ # We handle this by making strikethrough part of the main element,
9
+ # but using an extra wrapping element for underline.
10
+
11
+ tag = props["tag"] || "p"
12
+
13
+ para_styles, extra_styles = para_style(props)
14
+
8
15
  HTML.render do |h|
9
- h.p(**para_options(props), &block)
16
+ if extra_styles.empty?
17
+ h.send(tag, id: html_id, style: para_styles, &block)
18
+ else
19
+ h.div(id: html_id, style: extra_styles.merge(width: "100%")) do
20
+ h.send(tag, style: para_styles, &block)
21
+ end
22
+ end
10
23
  end
11
24
  end
12
25
 
13
26
  private
14
27
 
15
- def para_options(props)
16
- (props["html_attributes"] || {}).merge(id: html_id, style: para_style(props))
28
+ def para_style(props)
29
+
30
+ ds = drawable_style(props)
31
+ s1, s2 = text_specific_styles(props)
32
+ [ds.merge(s1), s2]
17
33
  end
18
34
 
19
- def para_style(props)
20
- drawable_style(props).merge({
21
- color: rgb_to_hex(props["stroke"]),
35
+ def text_specific_styles(props)
36
+ # Shoes3 allows align: right on TextDrawables like em(), but it does
37
+ # nothing. We can ignore it or (maybe in future?) warn if we see it.
38
+
39
+ strikethrough = props["strikethrough"]
40
+ strikethrough = nil if strikethrough == "" || strikethrough == "none"
41
+ s1 = {
42
+ "font-variant": props["font_variant"],
43
+ "color": rgb_to_hex(props["stroke"]),
44
+ "background-color": rgb_to_hex(props["fill"]),
22
45
  "font-size": para_font_size(props),
23
- "font-family": props["font"],
24
- }.compact)
46
+ "font-family": props["family"],
47
+ "text-decoration-line": strikethrough ? "line-through" : nil,
48
+ "text-decoration-color": props["strikecolor"] ? rgb_to_hex(props["strikecolor"]) : nil,
49
+ "font-weight": props["font_weight"]? props["font_weight"] : nil,
50
+ :'font-style' => case props["emphasis"]
51
+ when "normal"
52
+ "normal"
53
+ when "oblique"
54
+ "oblique"
55
+ when "italic"
56
+ "italic"
57
+ else
58
+ nil
59
+ end,
60
+ :'letter-spacing' => props["kerning"] ? "#{props["kerning"]}px" : nil,
61
+ :'vertical-align' => props["rise"] ? "#{props["rise"]}px" : nil
62
+ }.compact
63
+
64
+ s2 = {}
65
+ if props["align"] && props["align"] != ""
66
+ s2[:"text-align"] = props["align"]
67
+ end
68
+
69
+ unless [nil, "none"].include?(props["underline"])
70
+ undercolor = rgb_to_hex props["undercolor"]
71
+ s2["text-decoration-color"] = undercolor if undercolor
72
+ end
73
+
74
+ # [nil, "none", "single", "double", "low", "error"]
75
+ case props["underline"]
76
+ when nil, "none"
77
+ # Do nothing
78
+ when "single"
79
+ s2["text-decoration-line"] = "underline"
80
+ when "double"
81
+ s2["text-decoration-line"] = "underline"
82
+ s2["text-decoration-style"] = "double"
83
+ when "error"
84
+ s2["text-decoration-line"] = "underline"
85
+ s2["text-decoration-style"] = "wavy"
86
+ when "low"
87
+ s2["text-decoration-line"] = "underline"
88
+ s2["text-underline-offset"] = "0.3rem"
89
+ else
90
+ # This should normally be unreachable
91
+ raise Shoes::Errors::InvalidAttributeValueError, "Unexpected underline type #{props["underline"].inspect}!"
92
+ end
93
+
94
+ [s1, s2]
95
+ end
96
+
97
+
98
+ def parse_font(props)
99
+
100
+ def contains_number?(str)
101
+ !!(str =~ /\d/)
102
+ end
103
+ def contains_only_numbers?(string)
104
+ /^\d+\z/ =~ string
105
+ end
106
+
107
+
108
+ input = props["font"]
109
+ regex = /\s+(?=(?:[^']*'[^']*')*[^']*$)(?![^']*,[^']*')/
110
+ result = input.split(regex)
111
+
112
+ fs = "normal"
113
+ fv = "normal"
114
+ fw = "normal"
115
+ fss = "medium"
116
+ ff = "Arial"
117
+
118
+ fos = ["italic", "oblique"]
119
+ fov = ["small-caps", "initial", "inherit"]
120
+ fow = ["bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900"]
121
+ foss = ["xx-small", "x-small", "small","large", "x-large", "xx-large", "smaller", "larger"]
122
+
123
+ result.each do |i|
124
+ if fos.include?(i)
125
+ fs = i
126
+ next
127
+ elsif fov.include?(i)
128
+ fv = i
129
+ next
130
+ elsif fow.include?(i)
131
+ fw = i
132
+ next
133
+ elsif foss.include?(i)
134
+ fss = i
135
+ next
136
+ else
137
+ if contains_number?(i)
138
+
139
+ if contains_only_numbers?(i)
140
+ fss = i + "px"
141
+ else
142
+ fss = i
143
+ end
144
+
145
+ elsif i != "normal" && i != "medium" && i.strip != ""
146
+
147
+ if ff == "Arial"
148
+
149
+ ff = i
150
+
151
+ else
152
+
153
+ ff = ff+ i
154
+
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+
162
+ "#{fs} #{fv} #{fw} #{fss} #{ff}"
163
+
25
164
  end
26
165
 
27
166
  def para_font_size(props)
@@ -32,4 +171,67 @@ module Scarpe::Components::Calzini
32
171
 
33
172
  dimensions_length(font_size)
34
173
  end
174
+
175
+ public
176
+
177
+ # The text element is used to render the equivalent of Shoes cText,
178
+ # which includes em, strong, span, link and so on. We use a
179
+ # "content" tag for it which alternates plaintext with a hash of
180
+ # properties.
181
+ def text_drawable_element(prop_array)
182
+ out = String.new # Need unfrozen string
183
+
184
+ # Each item should be a String or a property Hash
185
+ # :items, :html_id, :tag, :props
186
+ prop_array.each do |item|
187
+ if item.is_a?(String)
188
+ out << item.gsub("\n", "<br/>")
189
+ else
190
+ s, extra = text_drawable_style(item[:props])
191
+ out << HTML.render do |h|
192
+ if extra.empty?
193
+ h.send(
194
+ item[:tag] || "span",
195
+ class: "id_#{item[:html_id]}",
196
+ style: s,
197
+ **text_drawable_attrs(item[:props])
198
+ ) do
199
+ text_drawable_element(item[:items])
200
+ end
201
+ else
202
+ h.span(class: "id_#{item[:html_id]}", style: extra) do
203
+ h.send(
204
+ item[:tag] || "span",
205
+ class: "id_#{item[:html_id]}",
206
+ style: s,
207
+ **text_drawable_attrs(item[:props])
208
+ ) do
209
+ text_drawable_element(item[:items])
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ out
218
+ end
219
+
220
+ private
221
+
222
+ def text_drawable_attrs(props)
223
+ {
224
+ # These properties will normally only be set by link()
225
+ href: props["click"],
226
+ onclick: props["has_block"] ? handler_js_code("click") : nil,
227
+ }.compact
228
+ end
229
+
230
+ def text_drawable_style(props)
231
+ s, extra_s = text_specific_styles(props)
232
+
233
+ # Add hover styles, perhaps with CSS pseudo-class
234
+
235
+ [drawable_style(props).merge(s), extra_s]
236
+ end
35
237
  end
@@ -3,27 +3,30 @@
3
3
  module Scarpe::Components::Calzini
4
4
  def slot_element(props, &block)
5
5
  HTML.render do |h|
6
- h.div((props["html_attributes"] || {}).merge(id: html_id, style: slot_style(props)), &block)
6
+ h.div((props["html_attributes"] || {}).merge(id: html_id, style: slot_style(props))) do
7
+ h.div(style: { height: "100%", width: "100%" }, &block)
8
+ end
7
9
  end
8
10
  end
9
11
 
10
12
  def flow_element(props, &block)
11
13
  HTML.render do |h|
12
- h.div((props["html_attributes"] || {}).merge(id: html_id, style: flow_style(props)), &block)
14
+ h.div((props["html_attributes"] || {}).merge(id: html_id, style: flow_style(props))) do
15
+ h.div(style: { height: "100%", width: "100%", position: "relative" }, &block)
16
+ end
13
17
  end
14
18
  end
15
19
 
16
20
  def stack_element(props, &block)
17
21
  HTML.render do |h|
18
- h.div((props["html_attributes"] || {}).merge(id: html_id, style: stack_style(props)), &block)
22
+ h.div((props["html_attributes"] || {}).merge(id: html_id, style: stack_style(props))) do
23
+ h.div(style: { height: "100%", width: "100%", position: "relative" }, &block)
24
+ end
19
25
  end
20
26
  end
21
27
 
22
28
  def documentroot_element(props, &block)
23
- HTML.render do |h|
24
- # DocumentRoot rendering intentionally uses flow styles.
25
- h.div((props["html_attributes"] || {}).merge(id: html_id, style: flow_style(props)), &block)
26
- end
29
+ flow_element(props, &block)
27
30
  end
28
31
 
29
32
  private
@@ -32,12 +35,14 @@ module Scarpe::Components::Calzini
32
35
  styles = drawable_style(props)
33
36
  styles = background_style(props, styles)
34
37
  styles = border_style(props, styles)
35
- styles = spacing_styles_for_attr("margin", props, styles)
36
- styles = spacing_styles_for_attr("padding", props, styles)
37
38
 
38
39
  styles[:width] = dimensions_length(props["width"]) if props["width"]
39
40
  styles[:height] = dimensions_length(props["height"]) if props["height"]
40
41
 
42
+ ## A new slot defines a new coordinate system, so absolutely-positioned children
43
+ ## are relative to it. But that's going to need a lot of testing and Shoes3 comparison.
44
+ #styles[:position] = "relative"
45
+
41
46
  styles
42
47
  end
43
48
 
@@ -101,55 +106,4 @@ module Scarpe::Components::Calzini
101
106
 
102
107
  styles.merge(background: color)
103
108
  end
104
-
105
- SPACING_DIRECTIONS = [:left, :right, :top, :bottom]
106
-
107
- # We extract the appropriate margin and padding from the margin and
108
- # padding properties. If there are no margin or padding properties,
109
- # we fall back to props["options"] margin or padding, if it exists.
110
- #
111
- # Margin or padding (in either props or props["options"]) can be
112
- # a Hash with directions as keys, or an Array of left/right/top/bottom,
113
- # or a constant, which means all four are that constant. You can
114
- # also specify a "margin" plus "margin-top" which is constant but
115
- # margin-top is overridden, or similar.
116
- #
117
- # If any margin or padding property exists in props then we don't
118
- # check props["options"].
119
- def spacing_styles_for_attr(attr, props, styles, with_options: true)
120
- spacing_styles = {}
121
-
122
- case props[attr]
123
- when Hash
124
- props[attr].each do |dir, value|
125
- spacing_styles[:"#{attr}-#{dir}"] = dimensions_length value
126
- end
127
- when Array
128
- SPACING_DIRECTIONS.zip(props[attr]).to_h.compact.each do |dir, value|
129
- spacing_styles[:"#{attr}-#{dir}"] = dimensions_length(value)
130
- end
131
- when String, Numeric
132
- spacing_styles[attr.to_sym] = dimensions_length(props[attr])
133
- end
134
-
135
- SPACING_DIRECTIONS.each do |dir|
136
- if props["#{attr}_#{dir}"]
137
- spacing_styles[:"#{attr}-#{dir}"] = dimensions_length props["#{attr}_#{dir}"]
138
- end
139
- end
140
-
141
- unless spacing_styles.empty?
142
- return styles.merge(spacing_styles)
143
- end
144
-
145
- # We should see if there are spacing properties in props["options"],
146
- # unless we're currently doing that.
147
- if with_options && props["options"]
148
- spacing_styles = spacing_styles_for_attr(attr, props["options"], {}, with_options: false)
149
- styles.merge spacing_styles
150
- else
151
- # No "options" or we already checked it? Return the styles we were given.
152
- styles
153
- end
154
- end
155
109
  end
@@ -68,6 +68,10 @@ module Scarpe::Components::Calzini
68
68
  p {
69
69
  margin: 0;
70
70
  }
71
+ #wrapper-wvroot {
72
+ height: 100%;
73
+ width: 100%;
74
+ }
71
75
  </style>
72
76
  </head>
73
77
  <body id='body-wvroot'>
@@ -110,13 +114,96 @@ module Scarpe::Components::Calzini
110
114
  if props["hidden"]
111
115
  styles[:display] = "none"
112
116
  end
117
+
118
+ # Do we need to set CSS positioning here, especially if displace is set? Position: relative maybe?
119
+ # We need some Shoes3 screenshots and HTML-based tests here...
120
+
121
+ if props["top"] || props["left"]
122
+ styles[:position] = "absolute"
123
+ end
124
+
125
+ styles[:top] = dimensions_length(props["top"]) if props["top"]
126
+ styles[:left] = dimensions_length(props["left"]) if props["left"]
127
+ styles[:width] = dimensions_length(props["width"]) if props["width"]
128
+ styles[:height] = dimensions_length(props["height"]) if props["height"]
129
+ styles[:"margin-left"] = dimensions_length(props["margin_left"]) if props["margin_left"]
130
+ styles[:"margin-right"] = dimensions_length(props["margin_right"]) if props["margin_right"]
131
+ styles[:"margin-top"] = dimensions_length(props["margin_top"]) if props["margin_top"]
132
+ styles[:"margin-bottom"] = dimensions_length(props["margin_bottom"]) if props["margin_bottom"]
133
+
134
+
135
+
136
+ styles = spacing_styles_for_attr("padding", props, styles)
137
+
113
138
  styles
114
139
  end
115
140
 
141
+ SPACING_DIRECTIONS = [:left, :right, :top, :bottom]
142
+
143
+ # We extract the appropriate margin and padding from the margin and
144
+ # padding properties. If there are no margin or padding properties,
145
+ # we fall back to props["options"] margin or padding, if it exists.
146
+ #
147
+ # Margin or padding (in either props or props["options"]) can be
148
+ # a Hash with directions as keys, or an Array of left/right/top/bottom,
149
+ # or a constant, which means all four are that constant. You can
150
+ # also specify a "margin" plus "margin-top" which is constant but
151
+ # margin-top is overridden, or similar.
152
+ #
153
+ # If any margin or padding property exists in props then we don't
154
+ # check props["options"].
155
+ def spacing_styles_for_attr(attr, props, styles, with_options: true)
156
+ spacing_styles = {}
157
+
158
+ case props[attr]
159
+ when Hash
160
+ props[attr].each do |dir, value|
161
+ spacing_styles[:"#{attr}-#{dir}"] = dimensions_length value
162
+ end
163
+ when Array
164
+ SPACING_DIRECTIONS.zip(props[attr]).to_h.compact.each do |dir, value|
165
+ spacing_styles[:"#{attr}-#{dir}"] = dimensions_length(value)
166
+ end
167
+ when String, Numeric
168
+ spacing_styles[attr.to_sym] = dimensions_length(props[attr])
169
+ end
170
+
171
+ SPACING_DIRECTIONS.each do |dir|
172
+ if props["#{attr}_#{dir}"]
173
+ spacing_styles[:"#{attr}-#{dir}"] = dimensions_length props["#{attr}_#{dir}"]
174
+ end
175
+ end
176
+
177
+ unless spacing_styles.empty?
178
+ return styles.merge(spacing_styles)
179
+ end
180
+
181
+ # We should see if there are spacing properties in props["options"],
182
+ # unless we're currently doing that.
183
+ if with_options && props["options"]
184
+ spacing_styles = spacing_styles_for_attr(attr, props["options"], {}, with_options: false)
185
+ styles.merge spacing_styles
186
+ else
187
+ # No "options" or we already checked it? Return the styles we were given.
188
+ styles
189
+ end
190
+ end
191
+
192
+ def first_color_of(*colors)
193
+ colors.compact!
194
+ colors.select! { |c| c != "" }
195
+ rgb_to_hex(colors[0])
196
+ end
197
+
116
198
  # Convert an [r, g, b, a] array to an HTML hex color code
117
199
  # Arrays support alpha. HTML hex does not. So premultiply.
118
200
  def rgb_to_hex(color)
119
- return color if color.nil?
201
+ return nil if color.nil?
202
+ return "#000000" if color == ""
203
+
204
+ # TODO: need to figure out if it's a color name like "aquamarine"
205
+ # or a hex code or an image file to use as a pattern or what.
206
+ return color if color.is_a?(String)
120
207
 
121
208
  r, g, b, a = *color
122
209
  if r.is_a?(Float)
@@ -3,9 +3,13 @@
3
3
  # Also defined in scarpe_core
4
4
  class Scarpe::Error < StandardError; end
5
5
 
6
+ # TODO: this should be under Scarpe::Errors, and also probably merged into the normal
7
+ # Scarpe errors file.
6
8
  module Scarpe
7
9
  class InternalError < Scarpe::Error; end
8
10
 
11
+ class OperationNotAllowedError < Scarpe::Error; end
12
+
9
13
  class FileContentError < Scarpe::Error; end
10
14
 
11
15
  class NoOperationError < Scarpe::Error; end
@@ -20,6 +20,9 @@ class Scarpe::Components::HTML
20
20
  :u,
21
21
  :line,
22
22
  :span,
23
+ :sub,
24
+ :sup,
25
+ :del,
23
26
  :svg,
24
27
  :h1,
25
28
  :h2,
@@ -27,7 +30,7 @@ class Scarpe::Components::HTML
27
30
  :h4,
28
31
  :h5,
29
32
  ].freeze
30
- VOID_TAGS = [:input, :img, :polygon, :source, :link, :path, :rect].freeze
33
+ VOID_TAGS = [:input, :img, :polygon, :source, :link, :path, :rect, :ellipse].freeze
31
34
 
32
35
  TAGS = (CONTENT_TAGS + VOID_TAGS).freeze
33
36
 
@@ -24,11 +24,18 @@ module Minitest
24
24
  # the reporter will be automatically overridden and print to console instead.
25
25
  #
26
26
  # Based on https://gist.github.com/davidwessman/09a13840a8a80080e3842ac3051714c7
27
- class ShoesExportReporter < DefaultReporter
27
+ class ShoesExportReporter < BaseReporter
28
+ class << self
29
+ attr_accessor :metadata
30
+ attr_accessor :export_file
31
+ end
32
+
28
33
  def self.activate!
29
34
  unless ENV["SHOES_MINITEST_EXPORT_FILE"]
30
35
  raise "ShoesExportReporter is available, but no export file was specified! Set SHOES_MINITEST_EXPORT_FILE!"
31
36
  end
37
+ ShoesExportReporter.export_file = File.expand_path(ENV["SHOES_MINITEST_EXPORT_FILE"])
38
+ ShoesExportReporter.metadata ||= {}
32
39
 
33
40
  Minitest::Reporters.use!
34
41
  end
@@ -58,6 +65,7 @@ module Minitest
58
65
  failures: failures,
59
66
  time: result.time,
60
67
  metadata: result.respond_to?(:metadata) ? result.metadata : {},
68
+ exporter_metadata: ShoesExportReporter.metadata,
61
69
  source_location: begin
62
70
  result.source_location
63
71
  rescue
@@ -66,8 +74,8 @@ module Minitest
66
74
  }
67
75
  end
68
76
 
69
- out_file = File.expand_path ENV["SHOES_MINITEST_EXPORT_FILE"]
70
- puts "Writing Minitest results to #{out_file.inspect}."
77
+ out_file = ShoesExportReporter.export_file
78
+ #puts "Writing Minitest results to #{out_file.inspect}."
71
79
  File.write(out_file, JSON.dump(results))
72
80
  end
73
81
  end
@@ -56,6 +56,47 @@ class Scarpe::Components::MinitestResult
56
56
  end
57
57
  end
58
58
 
59
+ def one_word_result
60
+ return "error" if self.error?
61
+ return "fail" if self.fail?
62
+ return "skip" if self.skip?
63
+ "success"
64
+ end
65
+
66
+ def result_and_message
67
+ return ["error", error_message] if self.error?
68
+ return ["fail", fail_message] if self.fail?
69
+ return ["skip", skip_message] if self.skip?
70
+ ["success", "OK"]
71
+ end
72
+
73
+ def console_summary
74
+ return "Error(s): #{@exceptions.inspect}" if self.error?
75
+ return "Failure: #{@failures.inspect}" if self.fail?
76
+ return "Skip: #{skip_message.inspect}" if self.skip?
77
+ "Success!"
78
+ end
79
+
80
+ def check(expect_result: :success, min_asserts: nil, max_asserts: nil)
81
+ unless [:error, :fail, :skip, :success].include?(expect_result)
82
+ raise Scarpe::InternalError, "Expected test result should be one of [:success, :fail, :error, :skip]!"
83
+ end
84
+
85
+ res, msg = result_and_message
86
+ if expect_result.to_s != res
87
+ return [false, "Expected #{expect_result} but got #{res}: #{msg}!"]
88
+ end
89
+
90
+ if min_asserts && @assertions < min_asserts
91
+ return [false, "Expected success with at least #{min_asserts} assertions but found only #{@assertions}!"]
92
+ end
93
+ if max_asserts && @assertions > max_asserts
94
+ return [false, "Expected success with no more than #{max_asserts} assertions but found only #{@assertions}!"]
95
+ end
96
+
97
+ [true, ""]
98
+ end
99
+
59
100
  def error?
60
101
  !@exceptions.empty?
61
102
  end
@@ -11,26 +11,41 @@ class Scarpe::Components::PrintLogImpl
11
11
  class PrintLogger
12
12
  class << self
13
13
  attr_accessor :silence
14
+ attr_accessor :min_level
14
15
  end
15
16
 
17
+ LEVELS = {
18
+ :never => 1000,
19
+ :error => 4,
20
+ :warn => 3,
21
+ :info => 2,
22
+ :debug => 1,
23
+ :always => -1,
24
+ }
25
+ PrintLogger.min_level = LEVELS[:always]
26
+
16
27
  def initialize(component_name)
17
28
  @comp_name = component_name
18
29
  end
19
30
 
20
31
  def error(msg)
21
- puts "#{@comp_name} error: #{msg}" unless PrintLogger.silence
32
+ return if PrintLogger.silence || PrintLogger.min_level > LEVELS[:error]
33
+ puts "#{@comp_name} error: #{msg}"
22
34
  end
23
35
 
24
36
  def warn(msg)
37
+ return if PrintLogger.silence || PrintLogger.min_level > LEVELS[:warn]
25
38
  puts "#{@comp_name} warn: #{msg}" unless PrintLogger.silence
26
39
  end
27
40
 
28
41
  def debug(msg)
42
+ return if PrintLogger.silence || PrintLogger.min_level > LEVELS[:debug]
29
43
  puts "#{@comp_name} debug: #{msg}" unless PrintLogger.silence
30
44
  end
31
45
 
32
46
  def info(msg)
33
- puts "#{@comp_name} info: #{msg}" unless PrintLogger.silence
47
+ return if PrintLogger.silence || PrintLogger.min_level > LEVELS[:info]
48
+ puts "#{@comp_name} info: #{msg}"
34
49
  end
35
50
  end
36
51