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.
- checksums.yaml +4 -4
- data/Gemfile +4 -1
- data/Gemfile.lock +2 -3
- data/README.md +2 -2
- data/assets/bootstrap-themes/bootstrap-cerulean.css +12229 -0
- data/assets/bootstrap-themes/bootstrap-cosmo.css +11810 -0
- data/assets/bootstrap-themes/bootstrap-cyborg.css +12210 -0
- data/assets/bootstrap-themes/bootstrap-darkly.css +12153 -0
- data/assets/bootstrap-themes/bootstrap-flatly.css +12126 -0
- data/assets/bootstrap-themes/bootstrap-icons.min.css +5 -0
- data/assets/bootstrap-themes/bootstrap-journal.css +12099 -0
- data/assets/bootstrap-themes/bootstrap-litera.css +12211 -0
- data/assets/bootstrap-themes/bootstrap-lumen.css +12369 -0
- data/assets/bootstrap-themes/bootstrap-lux.css +11928 -0
- data/assets/bootstrap-themes/bootstrap-materia.css +13184 -0
- data/assets/bootstrap-themes/bootstrap-minty.css +12177 -0
- data/assets/bootstrap-themes/bootstrap-morph.css +12750 -0
- data/assets/bootstrap-themes/bootstrap-pulse.css +11890 -0
- data/assets/bootstrap-themes/bootstrap-quartz.css +12622 -0
- data/assets/bootstrap-themes/bootstrap-sandstone.css +12201 -0
- data/assets/bootstrap-themes/bootstrap-simplex.css +12186 -0
- data/assets/bootstrap-themes/bootstrap-sketchy.css +12451 -0
- data/assets/bootstrap-themes/bootstrap-slate.css +12492 -0
- data/assets/bootstrap-themes/bootstrap-solar.css +12149 -0
- data/assets/bootstrap-themes/bootstrap-spacelab.css +12266 -0
- data/assets/bootstrap-themes/bootstrap-superhero.css +12216 -0
- data/assets/bootstrap-themes/bootstrap-united.css +12077 -0
- data/assets/bootstrap-themes/bootstrap-vapor.css +12549 -0
- data/assets/bootstrap-themes/bootstrap-yeti.css +12325 -0
- data/assets/bootstrap-themes/bootstrap-zephyr.css +12283 -0
- data/assets/bootstrap-themes/bootstrap.bundle.min.js +7 -0
- data/lib/scarpe/components/asset_server.rb +219 -0
- data/lib/scarpe/components/base64.rb +22 -0
- data/lib/scarpe/components/calzini/{art_widgets.rb → art_drawables.rb} +42 -18
- data/lib/scarpe/components/calzini/border.rb +38 -0
- data/lib/scarpe/components/calzini/button.rb +6 -8
- data/lib/scarpe/components/calzini/misc.rb +7 -17
- data/lib/scarpe/components/calzini/para.rb +213 -11
- data/lib/scarpe/components/calzini/slots.rb +14 -60
- data/lib/scarpe/components/calzini.rb +88 -1
- data/lib/scarpe/components/errors.rb +4 -0
- data/lib/scarpe/components/html.rb +4 -1
- data/lib/scarpe/components/minitest_export_reporter.rb +11 -3
- data/lib/scarpe/components/minitest_result.rb +41 -0
- data/lib/scarpe/components/print_logger.rb +17 -2
- data/lib/scarpe/components/process_helpers.rb +37 -0
- data/lib/scarpe/components/segmented_file_loader.rb +1 -1
- data/lib/scarpe/components/tiranti.rb +42 -100
- data/lib/scarpe/components/unit_test_helpers.rb +3 -1
- data/lib/scarpe/components/version.rb +1 -1
- metadata +34 -6
- 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 =
|
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
|
-
|
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
|
16
|
-
|
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
|
20
|
-
|
21
|
-
|
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["
|
24
|
-
|
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))
|
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))
|
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))
|
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
|
-
|
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
|
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 <
|
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 =
|
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
|
-
|
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
|
-
|
47
|
+
return if PrintLogger.silence || PrintLogger.min_level > LEVELS[:info]
|
48
|
+
puts "#{@comp_name} info: #{msg}"
|
34
49
|
end
|
35
50
|
end
|
36
51
|
|