inline_svg 1.0.0 → 1.10.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 +5 -5
- data/.github/workflows/integration_test.yml +47 -0
- data/.github/workflows/rails_6_webpacker_integration_tests.yaml +62 -0
- data/.github/workflows/ruby.yml +20 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +421 -0
- data/CHANGELOG.md +143 -3
- data/README.md +149 -30
- data/Rakefile +7 -0
- data/inline_svg.gemspec +2 -1
- data/lib/inline_svg/action_view/helpers.rb +68 -7
- data/lib/inline_svg/cached_asset_file.rb +71 -0
- data/lib/inline_svg/finds_asset_paths.rb +1 -1
- data/lib/inline_svg/id_generator.rb +12 -3
- data/lib/inline_svg/io_resource.rb +4 -3
- data/lib/inline_svg/propshaft_asset_finder.rb +16 -0
- data/lib/inline_svg/railtie.rb +8 -3
- data/lib/inline_svg/static_asset_finder.rb +5 -2
- data/lib/inline_svg/transform_pipeline/transformations/aria_attributes.rb +16 -19
- data/lib/inline_svg/transform_pipeline/transformations/aria_hidden.rb +9 -0
- data/lib/inline_svg/transform_pipeline/transformations/aria_hidden_attribute.rb +9 -0
- data/lib/inline_svg/transform_pipeline/transformations/class_attribute.rb +5 -6
- data/lib/inline_svg/transform_pipeline/transformations/data_attributes.rb +4 -5
- data/lib/inline_svg/transform_pipeline/transformations/description.rb +7 -6
- data/lib/inline_svg/transform_pipeline/transformations/height.rb +3 -4
- data/lib/inline_svg/transform_pipeline/transformations/id_attribute.rb +3 -4
- data/lib/inline_svg/transform_pipeline/transformations/no_comment.rb +4 -4
- data/lib/inline_svg/transform_pipeline/transformations/preserve_aspect_ratio.rb +3 -4
- data/lib/inline_svg/transform_pipeline/transformations/size.rb +4 -5
- data/lib/inline_svg/transform_pipeline/transformations/style_attribute.rb +11 -0
- data/lib/inline_svg/transform_pipeline/transformations/title.rb +7 -6
- data/lib/inline_svg/transform_pipeline/transformations/transformation.rb +13 -0
- data/lib/inline_svg/transform_pipeline/transformations/view_box.rb +9 -0
- data/lib/inline_svg/transform_pipeline/transformations/width.rb +3 -4
- data/lib/inline_svg/transform_pipeline/transformations.rb +11 -2
- data/lib/inline_svg/transform_pipeline.rb +1 -1
- data/lib/inline_svg/version.rb +1 -1
- data/lib/inline_svg/webpack_asset_finder.rb +60 -0
- data/lib/inline_svg.rb +46 -9
- data/spec/cached_asset_file_spec.rb +73 -0
- data/spec/files/static_assets/assets0/known-document-two.svg +1 -0
- data/spec/files/static_assets/assets0/known-document.svg +1 -0
- data/spec/files/static_assets/assets0/some-document.svg +1 -0
- data/spec/files/static_assets/assets1/known-document.svg +1 -0
- data/spec/files/static_assets/assets1/other-document.svg +3 -0
- data/spec/files/static_assets/assets1/some-file.txt +1 -0
- data/spec/finds_asset_paths_spec.rb +45 -0
- data/spec/helpers/inline_svg_spec.rb +117 -51
- data/spec/id_generator_spec.rb +5 -3
- data/spec/inline_svg_spec.rb +48 -0
- data/spec/propshaft_asset_finder_spec.rb +23 -0
- data/spec/static_asset_finder_spec.rb +25 -0
- data/spec/transformation_pipeline/transformations/aria_attributes_spec.rb +6 -6
- data/spec/transformation_pipeline/transformations/aria_hidden_attribute_spec.rb +12 -0
- data/spec/transformation_pipeline/transformations/height_spec.rb +9 -0
- data/spec/transformation_pipeline/transformations/style_attribute_spec.rb +26 -0
- data/spec/transformation_pipeline/transformations/title_spec.rb +9 -0
- data/spec/transformation_pipeline/transformations/transformation_spec.rb +39 -0
- data/spec/transformation_pipeline/transformations/view_box_spec.rb +13 -0
- data/spec/transformation_pipeline/transformations_spec.rb +7 -1
- data/spec/webpack_asset_finder_spec.rb +23 -0
- metadata +62 -10
- data/circle.yml +0 -3
@@ -0,0 +1 @@
|
|
1
|
+
<svg>interesting content</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3.273 7.151l-2.917-2.916c-.23.665-.356 1.361-.356 2.057 0 1.608.624 3.216 1.851 4.442 1.35 1.351 3.163 1.957 4.928 1.821.933-.072 1.851.268 2.513.93l9.646 9.646c.58.579 1.338.869 2.097.869 1.636 0 2.965-1.326 2.965-2.965 0-.759-.29-1.518-.868-2.097l-9.647-9.646c-.661-.662-1.002-1.581-.93-2.514.136-1.766-.47-3.578-1.821-4.928-.372-.372-.778-.686-1.209-.945l-6.252 6.246zm18.727 13.849c0 .552-.448 1-1 1s-1-.448-1-1 .448-1 1-1 1 .447 1 1zm-12.153-13.396l-3.061 3.061-2.566-2.567 3.062-3.061 2.565 2.567zm-.933.096l-.762.761-1.705-1.705.762-.762 1.705 1.706zm-2.991-.42l-.761.762 1.706 1.705.762-.762-1.707-1.705zm2.484-6.903l-2.893 2.893-2.412-2.412c.953-.556 2.044-.858 3.165-.858.707 0 1.425.12 2.128.373l.012.004z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg>Another known document</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
Some file contents.
|
@@ -45,4 +45,49 @@ describe InlineSvg::FindsAssetPaths do
|
|
45
45
|
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to be_nil
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
context "when propshaft finder returns an object which supports only the pathname method" do
|
50
|
+
it "returns fully qualified file paths from Propshaft" do
|
51
|
+
propshaft = double('PropshaftDouble')
|
52
|
+
|
53
|
+
expect(propshaft).to receive(:find_asset).with('some-file').
|
54
|
+
and_return(double(pathname: Pathname('/full/path/to/some-file')))
|
55
|
+
|
56
|
+
InlineSvg.configure do |config|
|
57
|
+
config.asset_finder = propshaft
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when webpack finder returns an object with a relative asset path" do
|
65
|
+
it "returns the fully qualified file path" do
|
66
|
+
webpacker = double('WebpackerDouble')
|
67
|
+
|
68
|
+
expect(webpacker).to receive(:find_asset).with('some-file').
|
69
|
+
and_return(double(filename: Pathname('/full/path/to/some-file')))
|
70
|
+
|
71
|
+
InlineSvg.configure do |config|
|
72
|
+
config.asset_finder = webpacker
|
73
|
+
end
|
74
|
+
|
75
|
+
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('/full/path/to/some-file')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when webpack finder returns an object with an absolute http asset path" do
|
80
|
+
it "returns the fully qualified file path" do
|
81
|
+
webpacker = double('WebpackerDouble')
|
82
|
+
|
83
|
+
expect(webpacker).to receive(:find_asset).with('some-file').
|
84
|
+
and_return(double(filename: Pathname('https://my-fancy-domain.test/full/path/to/some-file')))
|
85
|
+
|
86
|
+
InlineSvg.configure do |config|
|
87
|
+
config.asset_finder = webpacker
|
88
|
+
end
|
89
|
+
|
90
|
+
expect(InlineSvg::FindsAssetPaths.by_filename('some-file')).to eq Pathname('https://my-fancy-domain.test/full/path/to/some-file')
|
91
|
+
end
|
92
|
+
end
|
48
93
|
end
|
@@ -13,90 +13,138 @@ describe InlineSvg::ActionView::Helpers do
|
|
13
13
|
|
14
14
|
let(:helper) { ( Class.new { include InlineSvg::ActionView::Helpers } ).new }
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
shared_examples "inline_svg helper" do |helper_method:|
|
17
|
+
|
18
18
|
context "when passed the name of an SVG that does not exist" do
|
19
|
+
after(:each) do
|
20
|
+
InlineSvg.reset_configuration!
|
21
|
+
end
|
22
|
+
|
23
|
+
context "and configured to raise" do
|
24
|
+
it "raises an exception" do
|
25
|
+
InlineSvg.configure do |config|
|
26
|
+
config.raise_on_file_not_found = true
|
27
|
+
end
|
28
|
+
|
29
|
+
allow(InlineSvg::AssetFile).to receive(:named).
|
30
|
+
with('some-missing-file.svg').
|
31
|
+
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
32
|
+
|
33
|
+
expect {
|
34
|
+
helper.send(helper_method, 'some-missing-file.svg')
|
35
|
+
}.to raise_error(InlineSvg::AssetFile::FileNotFound)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
19
39
|
it "returns an empty, html safe, SVG document as a placeholder" do
|
20
40
|
allow(InlineSvg::AssetFile).to receive(:named).
|
21
41
|
with('some-missing-file.svg').
|
22
42
|
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
23
43
|
|
24
|
-
output = helper.
|
44
|
+
output = helper.send(helper_method, 'some-missing-file.svg')
|
25
45
|
expect(output).to eq "<svg><!-- SVG file not found: 'some-missing-file.svg' --></svg>"
|
26
46
|
expect(output).to be_html_safe
|
27
47
|
end
|
28
48
|
|
49
|
+
it "escapes malicious input" do
|
50
|
+
malicious = "--></svg><script>alert(1)</script><svg>.svg"
|
51
|
+
allow(InlineSvg::AssetFile).to receive(:named).
|
52
|
+
with(malicious).
|
53
|
+
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
54
|
+
|
55
|
+
output = helper.send(helper_method, malicious)
|
56
|
+
expect(output).to eq "<svg><!-- SVG file not found: '--></svg><script>alert(1)</script><svg>.svg' --></svg>"
|
57
|
+
expect(output).to be_html_safe
|
58
|
+
end
|
59
|
+
|
29
60
|
it "gives a helpful hint when no .svg extension is provided in the filename" do
|
30
61
|
allow(InlineSvg::AssetFile).to receive(:named).
|
31
62
|
with('missing-file-with-no-extension').
|
32
63
|
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
33
64
|
|
34
|
-
output = helper.
|
65
|
+
output = helper.send(helper_method, 'missing-file-with-no-extension')
|
35
66
|
expect(output).to eq "<svg><!-- SVG file not found: 'missing-file-with-no-extension' (Try adding .svg to your filename) --></svg>"
|
36
67
|
end
|
68
|
+
|
69
|
+
it "allows the CSS class on the empty SVG document to be changed" do
|
70
|
+
InlineSvg.configure do |config|
|
71
|
+
config.svg_not_found_css_class = 'missing-svg'
|
72
|
+
end
|
73
|
+
|
74
|
+
allow(InlineSvg::AssetFile).to receive(:named).
|
75
|
+
with('some-other-missing-file.svg').
|
76
|
+
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
77
|
+
|
78
|
+
output = helper.send(helper_method, 'some-other-missing-file.svg')
|
79
|
+
expect(output).to eq "<svg class='missing-svg'><!-- SVG file not found: 'some-other-missing-file.svg' --></svg>"
|
80
|
+
expect(output).to be_html_safe
|
81
|
+
end
|
82
|
+
|
83
|
+
context "and a fallback that does exist" do
|
84
|
+
it "displays the fallback" do
|
85
|
+
allow(InlineSvg::AssetFile).to receive(:named).
|
86
|
+
with('missing.svg').
|
87
|
+
and_raise(InlineSvg::AssetFile::FileNotFound.new)
|
88
|
+
|
89
|
+
fallback_file = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><!-- This is a comment --></svg>'
|
90
|
+
allow(InlineSvg::AssetFile).to receive(:named).with('fallback.svg').and_return(fallback_file)
|
91
|
+
expect(helper.send(helper_method, 'missing.svg', fallback: 'fallback.svg')).to eq fallback_file
|
92
|
+
end
|
93
|
+
end
|
37
94
|
end
|
38
95
|
|
39
96
|
context "when passed an existing SVG file" do
|
40
97
|
|
41
98
|
context "and no options" do
|
42
99
|
it "returns a html safe version of the file's contents" do
|
43
|
-
example_file =
|
44
|
-
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><!-- This is a comment --></svg>
|
45
|
-
SVG
|
100
|
+
example_file = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><!-- This is a comment --></svg>'
|
46
101
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(example_file)
|
47
|
-
expect(helper.
|
102
|
+
expect(helper.send(helper_method, 'some-file')).to eq example_file
|
48
103
|
end
|
49
104
|
end
|
50
105
|
|
51
106
|
context "and the 'title' option" do
|
52
107
|
it "adds the title node to the SVG output" do
|
53
|
-
input_svg =
|
54
|
-
<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"></svg>
|
55
|
-
SVG
|
56
|
-
expected_output = <<-SVG
|
57
|
-
<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"><title>A title</title></svg>
|
58
|
-
SVG
|
108
|
+
input_svg = '<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"></svg>'
|
109
|
+
expected_output = '<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"><title>A title</title></svg>'
|
59
110
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
60
|
-
expect(helper.
|
111
|
+
expect(helper.send(helper_method, 'some-file', title: 'A title')).to eq expected_output
|
61
112
|
end
|
62
113
|
end
|
63
114
|
|
64
115
|
context "and the 'desc' option" do
|
65
116
|
it "adds the description node to the SVG output" do
|
66
|
-
input_svg =
|
67
|
-
<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"></svg>
|
68
|
-
SVG
|
69
|
-
expected_output = <<-SVG
|
70
|
-
<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"><desc>A description</desc></svg>
|
71
|
-
SVG
|
117
|
+
input_svg = '<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"></svg>'
|
118
|
+
expected_output = '<svg xmlns="http://www.w3.org/2000/svg" role="presentation" xml:lang="en"><desc>A description</desc></svg>'
|
72
119
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
73
|
-
expect(helper.
|
120
|
+
expect(helper.send(helper_method, 'some-file', desc: 'A description')).to eq expected_output
|
74
121
|
end
|
75
122
|
end
|
76
123
|
|
77
124
|
context "and the 'nocomment' option" do
|
78
125
|
it "strips comments and other unknown/unsafe nodes from the output" do
|
79
|
-
input_svg =
|
80
|
-
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"
|
81
|
-
SVG
|
82
|
-
expected_output = <<-SVG
|
83
|
-
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"></svg>
|
84
|
-
SVG
|
126
|
+
input_svg = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><!-- This is a comment --></svg>'
|
127
|
+
expected_output = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"></svg>'
|
85
128
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
86
|
-
expect(helper.
|
129
|
+
expect(helper.send(helper_method, 'some-file', nocomment: true)).to eq expected_output
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "and the 'aria_hidden' option" do
|
134
|
+
it "sets 'aria-hidden=true' in the output" do
|
135
|
+
input_svg = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"></svg>'
|
136
|
+
expected_output = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en" aria-hidden="true"></svg>'
|
137
|
+
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
138
|
+
expect(helper.send(helper_method, 'some-file', aria_hidden: true)).to eq expected_output
|
87
139
|
end
|
88
140
|
end
|
89
141
|
|
90
142
|
context "and all options" do
|
91
143
|
it "applies all expected transformations to the output" do
|
92
|
-
input_svg =
|
93
|
-
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"
|
94
|
-
SVG
|
95
|
-
expected_output = <<-SVG
|
96
|
-
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><title>A title</title><desc>A description</desc></svg>
|
97
|
-
SVG
|
144
|
+
input_svg = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><!-- This is a comment --></svg>'
|
145
|
+
expected_output = '<svg xmlns="http://www.w3.org/2000/svg" xml:lang="en"><title>A title</title><desc>A description</desc></svg>'
|
98
146
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
99
|
-
expect(helper.
|
147
|
+
expect(helper.send(helper_method, 'some-file', title: 'A title', desc: 'A description', nocomment: true)).to eq expected_output
|
100
148
|
end
|
101
149
|
end
|
102
150
|
|
@@ -112,14 +160,10 @@ SVG
|
|
112
160
|
end
|
113
161
|
|
114
162
|
it "applies custm transformations to the output" do
|
115
|
-
input_svg =
|
116
|
-
<svg></svg>
|
117
|
-
SVG
|
118
|
-
expected_output = <<-SVG
|
119
|
-
<svg custom="some value"></svg>
|
120
|
-
SVG
|
163
|
+
input_svg = '<svg></svg>'
|
164
|
+
expected_output = '<svg custom="some value"></svg>'
|
121
165
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
122
|
-
expect(helper.
|
166
|
+
expect(helper.send(helper_method, 'some-file', custom: 'some value')).to eq expected_output
|
123
167
|
end
|
124
168
|
end
|
125
169
|
|
@@ -140,7 +184,7 @@ SVG
|
|
140
184
|
|
141
185
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
142
186
|
|
143
|
-
expect(helper.
|
187
|
+
expect(helper.send(helper_method, 'some-file')).to eq "<svg custom=\"default value\"></svg>"
|
144
188
|
end
|
145
189
|
end
|
146
190
|
|
@@ -150,7 +194,7 @@ SVG
|
|
150
194
|
|
151
195
|
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
152
196
|
|
153
|
-
expect(helper.
|
197
|
+
expect(helper.send(helper_method, 'some-file', custom: 'some value')).to eq "<svg custom=\"some value\"></svg>"
|
154
198
|
end
|
155
199
|
end
|
156
200
|
end
|
@@ -162,13 +206,13 @@ SVG
|
|
162
206
|
expect(InlineSvg::IOResource).to receive(:===).with(argument).and_return(true)
|
163
207
|
expect(InlineSvg::IOResource).to receive(:read).with(argument)
|
164
208
|
expect(InlineSvg::AssetFile).to_not receive(:named)
|
165
|
-
helper.
|
209
|
+
helper.send(helper_method, argument)
|
166
210
|
end
|
167
211
|
it 'accept filename' do
|
168
212
|
expect(InlineSvg::IOResource).to receive(:===).with(argument).and_return(false)
|
169
213
|
expect(InlineSvg::IOResource).to_not receive(:read)
|
170
214
|
expect(InlineSvg::AssetFile).to receive(:named).with(argument)
|
171
|
-
helper.
|
215
|
+
helper.send(helper_method, argument)
|
172
216
|
end
|
173
217
|
end
|
174
218
|
context 'when passed IO object argument' do
|
@@ -178,17 +222,39 @@ SVG
|
|
178
222
|
it 'return valid svg' do
|
179
223
|
expect(InlineSvg::IOResource).to receive(:===).with(io_object).and_return(true)
|
180
224
|
expect(InlineSvg::IOResource).to receive(:read).with(io_object).and_return("<svg><!-- Test IO --></svg>")
|
181
|
-
output = helper.
|
182
|
-
expect(output).to eq "<svg><!-- Test IO --></svg
|
225
|
+
output = helper.send(helper_method, io_object)
|
226
|
+
expect(output).to eq "<svg><!-- Test IO --></svg>"
|
183
227
|
expect(output).to be_html_safe
|
184
228
|
end
|
185
229
|
|
186
230
|
it 'return valid svg for file' do
|
187
|
-
output = helper.
|
188
|
-
expect(output).to eq "<svg xmlns=\"http://www.w3.org/2000/svg\" xml:lang=\"en\" role=\"presentation\"><!-- This is a test comment --></svg
|
231
|
+
output = helper.send(helper_method, File.new(file_path))
|
232
|
+
expect(output).to eq "<svg xmlns=\"http://www.w3.org/2000/svg\" xml:lang=\"en\" role=\"presentation\"><!-- This is a test comment --></svg>"
|
189
233
|
expect(output).to be_html_safe
|
190
234
|
end
|
191
235
|
|
192
236
|
end
|
237
|
+
|
238
|
+
context 'default output' do
|
239
|
+
it "returns an SVG tag without any pre or post whitespace characters" do
|
240
|
+
input_svg = '<svg></svg>'
|
241
|
+
|
242
|
+
allow(InlineSvg::AssetFile).to receive(:named).with('some-file').and_return(input_svg)
|
243
|
+
|
244
|
+
expect(helper.send(helper_method, 'some-file')).to eq "<svg></svg>"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe '#inline_svg' do
|
250
|
+
it_behaves_like "inline_svg helper", helper_method: :inline_svg
|
251
|
+
end
|
252
|
+
|
253
|
+
describe '#inline_svg_tag' do
|
254
|
+
it_behaves_like "inline_svg helper", helper_method: :inline_svg_tag
|
255
|
+
end
|
256
|
+
|
257
|
+
describe '#inline_svg_tag' do
|
258
|
+
it_behaves_like "inline_svg helper", helper_method: :inline_svg_pack_tag
|
193
259
|
end
|
194
260
|
end
|
data/spec/id_generator_spec.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require_relative '../lib/inline_svg/id_generator'
|
2
2
|
|
3
3
|
describe InlineSvg::IdGenerator do
|
4
|
-
it "generates a hexencoded ID based on a salt" do
|
5
|
-
|
6
|
-
|
4
|
+
it "generates a hexencoded ID based on a salt and a random value" do
|
5
|
+
randomizer = -> { "some-random-value" }
|
6
|
+
|
7
|
+
expect(InlineSvg::IdGenerator.generate("some-base", "some-salt", randomness: randomizer)).
|
8
|
+
to eq("at2c17mkqnvopy36iccxspura7wnreqf")
|
7
9
|
end
|
8
10
|
end
|
data/spec/inline_svg_spec.rb
CHANGED
@@ -13,6 +13,10 @@ class MyInvalidCustomTransformInstance
|
|
13
13
|
def self.create_with_value(value); end
|
14
14
|
end
|
15
15
|
|
16
|
+
class MyCustomAssetFile
|
17
|
+
def self.named(filename); end
|
18
|
+
end
|
19
|
+
|
16
20
|
describe InlineSvg do
|
17
21
|
describe "configuration" do
|
18
22
|
context "when a block is not given" do
|
@@ -42,6 +46,50 @@ describe InlineSvg do
|
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
49
|
+
context "configuring a custom asset file" do
|
50
|
+
it "falls back to the built-in asset file implementation by default" do
|
51
|
+
expect(InlineSvg.configuration.asset_file).to eq(InlineSvg::AssetFile)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "adds a collaborator that meets the interface specification" do
|
55
|
+
InlineSvg.configure do |config|
|
56
|
+
config.asset_file = MyCustomAssetFile
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(InlineSvg.configuration.asset_file).to eq MyCustomAssetFile
|
60
|
+
end
|
61
|
+
|
62
|
+
it "rejects a collaborator that does not conform to the interface spec" do
|
63
|
+
bad_asset_file = double("bad_asset_file")
|
64
|
+
|
65
|
+
expect do
|
66
|
+
InlineSvg.configure do |config|
|
67
|
+
config.asset_file = bad_asset_file
|
68
|
+
end
|
69
|
+
end.to raise_error(InlineSvg::Configuration::Invalid, /asset_file should implement the #named method/)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "rejects a collaborator that implements the correct interface with the wrong arity" do
|
73
|
+
bad_asset_file = double("bad_asset_file", named: nil)
|
74
|
+
|
75
|
+
expect do
|
76
|
+
InlineSvg.configure do |config|
|
77
|
+
config.asset_file = bad_asset_file
|
78
|
+
end
|
79
|
+
end.to raise_error(InlineSvg::Configuration::Invalid, /asset_file should implement the #named method with arity 1/)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "configuring the default svg-not-found class" do
|
84
|
+
it "sets the class name" do
|
85
|
+
InlineSvg.configure do |config|
|
86
|
+
config.svg_not_found_css_class = 'missing-svg'
|
87
|
+
end
|
88
|
+
|
89
|
+
expect(InlineSvg.configuration.svg_not_found_css_class).to eq 'missing-svg'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
45
93
|
context "configuring custom transformation" do
|
46
94
|
it "allows a custom transformation to be added" do
|
47
95
|
InlineSvg.configure do |config|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../lib/inline_svg'
|
2
|
+
|
3
|
+
describe InlineSvg::PropshaftAssetFinder do
|
4
|
+
context "when the file is not found" do
|
5
|
+
it "returns nil" do
|
6
|
+
stub_const('Rails', double('Rails').as_null_object)
|
7
|
+
expect(::Rails.application.assets.load_path).to receive(:find).with('some-file').and_return(nil)
|
8
|
+
|
9
|
+
expect(InlineSvg::PropshaftAssetFinder.find_asset('some-file').pathname).to be_nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when the file is found" do
|
14
|
+
it "returns fully qualified file paths from Propshaft" do
|
15
|
+
stub_const('Rails', double('Rails').as_null_object)
|
16
|
+
asset = double('Asset')
|
17
|
+
expect(asset).to receive(:path).and_return(Pathname.new('/full/path/to/some-file'))
|
18
|
+
expect(::Rails.application.assets.load_path).to receive(:find).with('some-file').and_return(asset)
|
19
|
+
|
20
|
+
expect(InlineSvg::PropshaftAssetFinder.find_asset('some-file').pathname).to eq Pathname('/full/path/to/some-file')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../lib/inline_svg'
|
2
|
+
|
3
|
+
describe InlineSvg::StaticAssetFinder do
|
4
|
+
context "when the file is not found" do
|
5
|
+
it "returns nil" do
|
6
|
+
stub_const('Rails', double('Rails').as_null_object)
|
7
|
+
expect(::Rails.application.config.assets).to receive(:compile).and_return(true)
|
8
|
+
|
9
|
+
expect(described_class.find_asset('some-file').pathname).to be_nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when the file is found" do
|
14
|
+
it "returns fully qualified file path from Sprockets" do
|
15
|
+
stub_const('Rails', double('Rails').as_null_object)
|
16
|
+
expect(::Rails.application.config.assets).to receive(:compile).and_return(true)
|
17
|
+
pathname = Pathname.new('/full/path/to/some-file')
|
18
|
+
asset = double('Asset')
|
19
|
+
expect(asset).to receive(:filename).and_return(pathname)
|
20
|
+
expect(::Rails.application.assets).to receive(:[]).with('some-file').and_return(asset)
|
21
|
+
|
22
|
+
expect(described_class.find_asset('some-file').pathname).to eq(pathname)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
1
|
+
require "inline_svg/transform_pipeline"
|
2
2
|
|
3
3
|
describe InlineSvg::TransformPipeline::Transformations::AriaAttributes do
|
4
4
|
it "adds a role attribute to the SVG document" do
|
5
|
-
document = Nokogiri::XML::Document.parse(
|
5
|
+
document = Nokogiri::XML::Document.parse("<svg>Some document</svg>")
|
6
6
|
transformation = InlineSvg::TransformPipeline::Transformations::AriaAttributes.create_with_value({})
|
7
7
|
|
8
8
|
expect(transformation.transform(document).to_html).to eq(
|
@@ -12,7 +12,7 @@ describe InlineSvg::TransformPipeline::Transformations::AriaAttributes do
|
|
12
12
|
|
13
13
|
context "aria-labelledby attribute" do
|
14
14
|
it "adds 'title' when a title element is present" do
|
15
|
-
document = Nokogiri::XML::Document.parse(
|
15
|
+
document = Nokogiri::XML::Document.parse("<svg><title>Some title</title>Some document</svg>")
|
16
16
|
transformation = InlineSvg::TransformPipeline::Transformations::AriaAttributes.create_with_value(true)
|
17
17
|
|
18
18
|
expect(InlineSvg::IdGenerator).to receive(:generate).with("title", "Some title").
|
@@ -24,7 +24,7 @@ describe InlineSvg::TransformPipeline::Transformations::AriaAttributes do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "adds 'desc' when a description element is present" do
|
27
|
-
document = Nokogiri::XML::Document.parse(
|
27
|
+
document = Nokogiri::XML::Document.parse("<svg><desc>Some description</desc>Some document</svg>")
|
28
28
|
transformation = InlineSvg::TransformPipeline::Transformations::AriaAttributes.create_with_value(true)
|
29
29
|
|
30
30
|
expect(InlineSvg::IdGenerator).to receive(:generate).with("desc", "Some description").
|
@@ -36,7 +36,7 @@ describe InlineSvg::TransformPipeline::Transformations::AriaAttributes do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
it "adds both 'desc' and 'title' when title and description elements are present" do
|
39
|
-
document = Nokogiri::XML::Document.parse(
|
39
|
+
document = Nokogiri::XML::Document.parse("<svg><title>Some title</title><desc>Some description</desc>Some document</svg>")
|
40
40
|
transformation = InlineSvg::TransformPipeline::Transformations::AriaAttributes.create_with_value(true)
|
41
41
|
|
42
42
|
expect(InlineSvg::IdGenerator).to receive(:generate).with("title", "Some title").
|
@@ -50,7 +50,7 @@ describe InlineSvg::TransformPipeline::Transformations::AriaAttributes do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it "uses existing IDs when they exist" do
|
53
|
-
document = Nokogiri::XML::Document.parse(
|
53
|
+
document = Nokogiri::XML::Document.parse("<svg><title id='my-title'>Some title</title><desc id='my-desc'>Some description</desc>Some document</svg>")
|
54
54
|
transformation = InlineSvg::TransformPipeline::Transformations::AriaAttributes.create_with_value(true)
|
55
55
|
|
56
56
|
expect(InlineSvg::IdGenerator).to receive(:generate).with("my-title", "Some title").
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'inline_svg/transform_pipeline'
|
2
|
+
|
3
|
+
describe InlineSvg::TransformPipeline::Transformations::AriaHiddenAttribute do
|
4
|
+
it "adds an aria-hidden='true' attribute to a SVG document" do
|
5
|
+
document = Nokogiri::XML::Document.parse('<svg>Some document</svg>')
|
6
|
+
transformation = InlineSvg::TransformPipeline::Transformations::AriaHiddenAttribute.create_with_value(true)
|
7
|
+
|
8
|
+
expect(transformation.transform(document).to_html).to eq(
|
9
|
+
"<svg aria-hidden=\"true\">Some document</svg>\n"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
@@ -9,4 +9,13 @@ describe InlineSvg::TransformPipeline::Transformations::Height do
|
|
9
9
|
"<svg height=\"5%\">Some document</svg>\n"
|
10
10
|
)
|
11
11
|
end
|
12
|
+
|
13
|
+
it "handles documents without SVG root elements" do
|
14
|
+
document = Nokogiri::XML::Document.parse("<foo>bar</foo><svg>Some document</svg>")
|
15
|
+
transformation = InlineSvg::TransformPipeline::Transformations::Height.create_with_value("5%")
|
16
|
+
|
17
|
+
expect(transformation.transform(document).to_html).to eq(
|
18
|
+
"<foo>bar</foo>\n"
|
19
|
+
)
|
20
|
+
end
|
12
21
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "inline_svg/transform_pipeline"
|
2
|
+
|
3
|
+
describe InlineSvg::TransformPipeline::Transformations::ClassAttribute do
|
4
|
+
it "adds a style attribute to a SVG document" do
|
5
|
+
document = Nokogiri::XML::Document.parse('<svg>Some document</svg>')
|
6
|
+
transformation =
|
7
|
+
InlineSvg::TransformPipeline::Transformations::StyleAttribute
|
8
|
+
.create_with_value("padding: 10px")
|
9
|
+
|
10
|
+
expect(transformation.transform(document).to_html).to eq(
|
11
|
+
"<svg style=\"padding: 10px\">Some document</svg>\n"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "preserves existing style attributes on a SVG document" do
|
16
|
+
xml = '<svg style="fill: red">Some document</svg>'
|
17
|
+
document = Nokogiri::XML::Document.parse(xml)
|
18
|
+
transformation =
|
19
|
+
InlineSvg::TransformPipeline::Transformations::StyleAttribute
|
20
|
+
.create_with_value("padding: 10px")
|
21
|
+
|
22
|
+
expect(transformation.transform(document).to_html).to eq(
|
23
|
+
"<svg style=\"fill: red;padding: 10px\">Some document</svg>\n"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
@@ -27,4 +27,13 @@ describe InlineSvg::TransformPipeline::Transformations::Title do
|
|
27
27
|
"<svg><title>Some Title</title></svg>\n"
|
28
28
|
)
|
29
29
|
end
|
30
|
+
|
31
|
+
it "handles non-ASCII characters" do
|
32
|
+
document = Nokogiri::XML::Document.parse('<svg>Some document</svg>')
|
33
|
+
transformation = InlineSvg::TransformPipeline::Transformations::Title.create_with_value("åäö")
|
34
|
+
|
35
|
+
expect(transformation.transform(document).to_html).to eq(
|
36
|
+
"<svg><title>åäö</title>Some document</svg>\n"
|
37
|
+
)
|
38
|
+
end
|
30
39
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'inline_svg'
|
2
|
+
require 'inline_svg/transform_pipeline'
|
3
|
+
|
4
|
+
describe InlineSvg::TransformPipeline::Transformations::Transformation do
|
5
|
+
context "#with_svg" do
|
6
|
+
it "returns a Nokogiri::XML::Document representing the parsed document fragment" do
|
7
|
+
document = Nokogiri::XML::Document.parse("<svg>Some document</svg>")
|
8
|
+
|
9
|
+
transformation = InlineSvg::TransformPipeline::Transformations::Transformation.new(:irrelevant)
|
10
|
+
expect(transformation.with_svg(document).to_html).to eq(
|
11
|
+
"<svg>Some document</svg>\n"
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "yields to the block when the document contains an SVG element" do
|
16
|
+
document = Nokogiri::XML::Document.parse("<svg>Some document</svg>")
|
17
|
+
svg = document.at_css("svg")
|
18
|
+
|
19
|
+
transformation = InlineSvg::TransformPipeline::Transformations::Transformation.new(:irrelevant)
|
20
|
+
|
21
|
+
returned_document = nil
|
22
|
+
expect do |b|
|
23
|
+
returned_document = transformation.with_svg(document, &b)
|
24
|
+
end.to yield_control
|
25
|
+
|
26
|
+
expect(returned_document.to_s).to match(/<svg>Some document<\/svg>/)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "does not yield if the document does not contain an SVG element at the root" do
|
30
|
+
document = Nokogiri::XML::Document.parse("<foo>bar</foo><svg>Some document</svg>")
|
31
|
+
|
32
|
+
transformation = InlineSvg::TransformPipeline::Transformations::Transformation.new(:irrelevant)
|
33
|
+
|
34
|
+
expect do |b|
|
35
|
+
transformation.with_svg(document, &b)
|
36
|
+
end.not_to yield_control
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'inline_svg/transform_pipeline'
|
2
|
+
|
3
|
+
describe InlineSvg::TransformPipeline::Transformations::ViewBox do
|
4
|
+
it "adds viewBox attribute to a SVG document" do
|
5
|
+
document = Nokogiri::XML::Document.parse('<svg>Some document</svg>')
|
6
|
+
transformation =
|
7
|
+
InlineSvg::TransformPipeline::Transformations::ViewBox
|
8
|
+
.create_with_value("0 0 100 100")
|
9
|
+
expect(transformation.transform(document).to_html).to eq(
|
10
|
+
"<svg viewBox=\"0 0 100 100\">Some document</svg>\n"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|