sass-embedded 0.1.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.
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ module Sass
6
+ class CustomImporterTest < MiniTest::Test
7
+ include TempFileTest
8
+
9
+ def setup
10
+ @compiler = Embedded::Compiler.new
11
+ end
12
+
13
+ def teardown
14
+ end
15
+
16
+ def render(data, importer)
17
+ @compiler.render({ data: data, importer: importer })[:css]
18
+ end
19
+
20
+ def test_custom_importer_works
21
+ temp_file("fonts.scss", ".font { color: $var1; }")
22
+
23
+ data = <<SCSS
24
+ @import "styles";
25
+ @import "fonts";
26
+ SCSS
27
+
28
+ output = render(data, [
29
+ lambda { |url, prev|
30
+ if url =~ /styles/
31
+ { contents: "$var1: #000; .hi { color: $var1; }" }
32
+ end
33
+ }
34
+ ])
35
+
36
+ assert_equal <<CSS.chomp, output
37
+ .hi {
38
+ color: #000;
39
+ }
40
+
41
+ .font {
42
+ color: #000;
43
+ }
44
+ CSS
45
+ end
46
+
47
+ def test_custom_importer_works_with_empty_contents
48
+ output = render("@import 'fake.scss';", [
49
+ lambda { |url, prev|
50
+ { contents: "" }
51
+ }
52
+ ])
53
+
54
+ assert_equal "", output
55
+ end
56
+
57
+ def test_custom_importer_works_with_file
58
+ temp_file("test.scss", ".test { color: #000; }")
59
+
60
+ output = render("@import 'fake.scss';", [
61
+ lambda { |url, prev|
62
+ { file: File.absolute_path("test.scss") }
63
+ }
64
+ ])
65
+
66
+ assert_equal <<CSS.chomp, output
67
+ .test {
68
+ color: #000;
69
+ }
70
+ CSS
71
+ end
72
+
73
+ def test_custom_importer_comes_after_local_file
74
+ temp_file("test.scss", ".test { color: #000; }")
75
+
76
+ output = render("@import 'test.scss';", [
77
+ lambda { |url, prev|
78
+ return { contents: '.h1 { color: #fff; }' }
79
+ }
80
+ ])
81
+
82
+ assert_equal <<CSS.chomp, output
83
+ .test {
84
+ color: #000;
85
+ }
86
+ CSS
87
+ end
88
+
89
+ def test_custom_importer_that_does_not_resolve
90
+ assert_raises(CompilationError) do
91
+ output = render("@import 'test.scss';", [
92
+ lambda { |url, prev|
93
+ return nil
94
+ }
95
+ ])
96
+ end
97
+ end
98
+
99
+ def test_custom_importer_that_returns_error
100
+ assert_raises(CompilationError) do
101
+ output = render("@import 'test.scss';", [
102
+ lambda { |url, prev|
103
+ IOError.new "test error"
104
+ }
105
+ ])
106
+ end
107
+ end
108
+
109
+ def test_custom_importer_that_raises_error
110
+ assert_raises(CompilationError) do
111
+ output = render("@import 'test.scss';", [
112
+ lambda { |url, prev|
113
+ raise IOError.new "test error"
114
+ }
115
+ ])
116
+ end
117
+ end
118
+
119
+ def test_parent_path_is_accessible
120
+ output = @compiler.render({
121
+ data: "@import 'parent.scss';",
122
+ file: "import-parent-filename.scss",
123
+ importer: [
124
+ lambda { |url, prev|
125
+ { contents: ".#{prev} { color: red; }" }
126
+ }
127
+ ]})[:css]
128
+
129
+ assert_equal <<CSS.chomp, output
130
+ .import-parent-filename.scss {
131
+ color: red;
132
+ }
133
+ CSS
134
+ end
135
+
136
+ def test_call_compiler_importer
137
+ output = @compiler.render({
138
+ data: "@import 'parent.scss';",
139
+ importer: [
140
+ lambda { |url, prev|
141
+ {
142
+ contents: @compiler.render({
143
+ data: "@import 'parent-parent.scss'",
144
+ importer: [
145
+ lambda { |url, prev|
146
+ { contents: 'h1 { color: black; }' }
147
+ }
148
+ ]})[:css]
149
+ }
150
+ }
151
+ ]})[:css]
152
+
153
+ assert_equal <<CSS.chomp, output
154
+ h1 {
155
+ color: black;
156
+ }
157
+ CSS
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ module Sass
6
+ class ErrorTest < MiniTest::Test
7
+
8
+ def setup
9
+ @compiler = Embedded::Compiler.new
10
+ end
11
+
12
+ def teardown
13
+ end
14
+
15
+ def test_first_backtrace_is_sass
16
+ begin
17
+ template = <<-SCSS
18
+ .foo {
19
+ baz: bang;
20
+ padding top: 10px;
21
+ }
22
+ SCSS
23
+
24
+ @compiler.render({
25
+ data: template,
26
+ })
27
+ rescue Sass::CompilationError => err
28
+ expected = "stdin:3:20"
29
+ assert_equal expected, err.backtrace.first
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,375 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require "stringio"
5
+
6
+ module Sass
7
+ class FunctionsTest < MiniTest::Test
8
+ def setup
9
+ @compiler = Embedded::Compiler.new
10
+ end
11
+
12
+ def teardown
13
+ end
14
+
15
+ def render(sass)
16
+ @compiler.render({
17
+ data: sass,
18
+ functions: {
19
+ 'javascript_path($path)': lambda { |path|
20
+ path.string.text = "/js/#{path.string.text}"
21
+ path
22
+ },
23
+ 'stylesheet_path($path)': lambda { |path|
24
+ path.string.text = "/css/#{path.string.text}"
25
+ path
26
+ },
27
+ 'sass_return_path($path)': lambda { |path|
28
+ path
29
+ },
30
+ 'no_return_path($path)': lambda { |path|
31
+ Sass::EmbeddedProtocol::Value.new(
32
+ :singleton => Sass::EmbeddedProtocol::SingletonValue::NULL
33
+ )
34
+ },
35
+ 'optional_arguments($path, $optional: null)': lambda { |path, optional|
36
+ Sass::EmbeddedProtocol::Value.new(
37
+ :string => Sass::EmbeddedProtocol::Value::String.new(
38
+ :text => "#{path.string.text}/#{optional.singleton == :NULL ? "bar" : optional.string.text}",
39
+ :quoted => true
40
+ )
41
+ )
42
+ },
43
+ 'function_that_raises_errors()': lambda {
44
+ raise StandardError, "Intentional wrong thing happened somewhere inside the custom function"
45
+ },
46
+ 'nice_color_argument($color)': lambda { |color|
47
+ color
48
+ },
49
+ 'returns_a_color()': lambda {
50
+ Sass::EmbeddedProtocol::Value.new(
51
+ :rgb_color => Sass::EmbeddedProtocol::Value::RgbColor.new(
52
+ :red => 0,
53
+ :green => 0,
54
+ :blue => 0,
55
+ :alpha => 1
56
+ )
57
+ )
58
+ },
59
+ 'returns_a_number()': lambda {
60
+ Sass::EmbeddedProtocol::Value.new(
61
+ :number => Sass::EmbeddedProtocol::Value::Number.new(
62
+ :value => -312,
63
+ :numerators => ['rem']
64
+ )
65
+ )
66
+ },
67
+ 'returns_a_bool()': lambda {
68
+ Sass::EmbeddedProtocol::Value.new(
69
+ :singleton => Sass::EmbeddedProtocol::SingletonValue::TRUE
70
+ )
71
+ },
72
+ 'inspect_bool($argument)': lambda { |argument|
73
+ raise StandardError.new "passed value is not a Sass::EmbeddedProtocol::SingletonValue::TRUE or Sass::EmbeddedProtocol::SingletonValue::FALSE" unless argument&.singleton == :TRUE || argument.singleton == :FALSE
74
+ argument
75
+ },
76
+ 'inspect_number($argument)': lambda { |argument|
77
+ raise StandardError.new "passed value is not a Sass::EmbeddedProtocol::Value::Number" unless argument&.number&.is_a? Sass::EmbeddedProtocol::Value::Number
78
+ argument
79
+ },
80
+ 'inspect_map($argument)': lambda { |argument|
81
+ raise StandardError.new "passed value is not a Sass::EmbeddedProtocol::Value::Map" unless argument&.map&.is_a? Sass::EmbeddedProtocol::Value::Map
82
+ argument
83
+ },
84
+ 'inspect_list($argument)': lambda { |argument|
85
+ raise StandardError.new "passed value is not a Sass::EmbeddedProtocol::Value::List" unless argument&.list&.is_a? Sass::EmbeddedProtocol::Value::List
86
+ argument
87
+ },
88
+ 'returns_sass_value()': lambda {
89
+ Sass::EmbeddedProtocol::Value.new(
90
+ :rgb_color => Sass::EmbeddedProtocol::Value::RgbColor.new(
91
+ :red => 0,
92
+ :green => 0,
93
+ :blue => 0,
94
+ :alpha => 1
95
+ )
96
+ )
97
+ },
98
+ 'returns_sass_map()': lambda {
99
+ Sass::EmbeddedProtocol::Value.new(
100
+ :map => Sass::EmbeddedProtocol::Value::Map.new(
101
+ :entries => [
102
+ Sass::EmbeddedProtocol::Value::Map::Entry.new(
103
+ :key => Sass::EmbeddedProtocol::Value.new(
104
+ :string => Sass::EmbeddedProtocol::Value::String.new(
105
+ :text => "color",
106
+ :quoted => true
107
+ )
108
+ ),
109
+ :value => Sass::EmbeddedProtocol::Value.new(
110
+ :rgb_color => Sass::EmbeddedProtocol::Value::RgbColor.new(
111
+ :red => 0,
112
+ :green => 0,
113
+ :blue => 0,
114
+ :alpha => 1
115
+ )
116
+ )
117
+ )
118
+ ]
119
+ )
120
+ )
121
+ },
122
+ 'returns_sass_list()': lambda {
123
+ Sass::EmbeddedProtocol::Value.new(
124
+ :list => Sass::EmbeddedProtocol::Value::List.new(
125
+ :separator => Sass::EmbeddedProtocol::ListSeparator::COMMA,
126
+ :has_brackets => true,
127
+ :contents => [
128
+ Sass::EmbeddedProtocol::Value.new(
129
+ :number => Sass::EmbeddedProtocol::Value::Number.new(
130
+ :value => 10
131
+ )
132
+ ),
133
+ Sass::EmbeddedProtocol::Value.new(
134
+ :number => Sass::EmbeddedProtocol::Value::Number.new(
135
+ :value => 20
136
+ )
137
+ ),
138
+ Sass::EmbeddedProtocol::Value.new(
139
+ :number => Sass::EmbeddedProtocol::Value::Number.new(
140
+ :value => 30
141
+ )
142
+ ),
143
+ ]
144
+ )
145
+ )
146
+ }
147
+ }
148
+ })[:css]
149
+ end
150
+
151
+ def test_functions_may_return_sass_string_type
152
+ assert_sass <<-SCSS, <<-CSS
153
+ div { url: url(sass_return_path("foo.svg")); }
154
+ SCSS
155
+ div { url: url("foo.svg"); }
156
+ CSS
157
+ end
158
+
159
+ def test_functions_work_with_varying_quotes_and_string_types
160
+ assert_sass <<-SCSS, <<-CSS
161
+ div {
162
+ url: url(asset-path("foo.svg"));
163
+ url: url(image-path("foo.png"));
164
+ url: url(video-path("foo.mov"));
165
+ url: url(audio-path("foo.mp3"));
166
+ url: url(font-path("foo.woff"));
167
+ url: url(javascript-path('foo.js'));
168
+ url: url(javascript-path("foo.js"));
169
+ url: url(stylesheet-path("foo.css"));
170
+ }
171
+ SCSS
172
+ div {
173
+ url: url(asset-path("foo.svg"));
174
+ url: url(image-path("foo.png"));
175
+ url: url(video-path("foo.mov"));
176
+ url: url(audio-path("foo.mp3"));
177
+ url: url(font-path("foo.woff"));
178
+ url: url("/js/foo.js");
179
+ url: url("/js/foo.js");
180
+ url: url("/css/foo.css");
181
+ }
182
+ CSS
183
+ end
184
+
185
+ def test_function_with_empty_unquoted_string
186
+ assert_sass <<-SCSS, <<-CSS
187
+ div {url: url(no-return-path('foo.svg'));}
188
+ SCSS
189
+ div { url: url(); }
190
+ CSS
191
+ end
192
+
193
+ def test_function_that_returns_a_color
194
+ assert_sass <<-SCSS, <<-CSS
195
+ div { background: returns-a-color(); }
196
+ SCSS
197
+ div { background: black; }
198
+ CSS
199
+ end
200
+
201
+ def test_function_that_returns_a_number
202
+ assert_sass <<-SCSS, <<-CSS
203
+ div { width: returns-a-number(); }
204
+ SCSS
205
+ div { width: -312rem; }
206
+ CSS
207
+ end
208
+
209
+ def test_function_that_takes_a_number
210
+ assert_sass <<-SCSS, <<-CSS
211
+ div { display: inspect-number(42.1px); }
212
+ SCSS
213
+ div { display: 42.1px; }
214
+ CSS
215
+ end
216
+
217
+ def test_function_that_returns_a_bool
218
+ assert_sass <<-SCSS, <<-CSS
219
+ div { width: returns-a-bool(); }
220
+ SCSS
221
+ div { width: true; }
222
+ CSS
223
+ end
224
+
225
+ def test_function_that_takes_a_bool
226
+ assert_sass <<-SCSS, <<-CSS
227
+ div { display: inspect-bool(true)}
228
+ SCSS
229
+ div { display: true; }
230
+ CSS
231
+ end
232
+
233
+ def test_function_with_optional_arguments
234
+ assert_sass <<-SCSS, <<-EXPECTED_CSS
235
+ div {
236
+ url: optional_arguments('first');
237
+ url: optional_arguments('second', 'qux');
238
+ }
239
+ SCSS
240
+ div {
241
+ url: "first/bar";
242
+ url: "second/qux";
243
+ }
244
+ EXPECTED_CSS
245
+ end
246
+
247
+ def test_functions_may_accept_sass_color_type
248
+ assert_sass <<-SCSS, <<-EXPECTED_CSS
249
+ div { color: nice_color_argument(red); }
250
+ SCSS
251
+ div { color: red; }
252
+ EXPECTED_CSS
253
+ end
254
+
255
+ def test_function_with_error
256
+ exception = assert_raises(Sass::CompilationError) do
257
+ render("div {url: function_that_raises_errors();}")
258
+ end
259
+ end
260
+
261
+ def test_function_that_returns_a_sass_value
262
+ assert_sass <<-SCSS, <<-CSS
263
+ div { background: returns-sass-value(); }
264
+ SCSS
265
+ div { background: black; }
266
+ CSS
267
+ end
268
+
269
+ def test_function_that_returns_a_sass_map
270
+ assert_sass <<-SCSS, <<-CSS
271
+ $my-map: returns-sass-map();
272
+ div { background: map-get( $my-map, color ); }
273
+ SCSS
274
+ div { background: black; }
275
+ CSS
276
+ end
277
+
278
+ def test_function_that_takes_a_sass_map
279
+ assert_sass <<-SCSS, <<-CSS
280
+ div { background-color: map-get( inspect-map(( color: black, number: 1.23px, string: "abc", map: ( x: 'y' ))), color ); }
281
+ SCSS
282
+ div { background-color: black; }
283
+ CSS
284
+ end
285
+
286
+ def test_function_that_returns_a_sass_list
287
+ assert_sass <<-SCSS, <<-CSS
288
+ $my-list: returns-sass-list();
289
+ div { width: nth( $my-list, 2 ); }
290
+ SCSS
291
+ div { width: 20; }
292
+ CSS
293
+ end
294
+
295
+ def test_function_that_takes_a_sass_list
296
+ assert_sass <<-SCSS, <<-CSS
297
+ div { width: nth(inspect-list((10 20 30)), 2); }
298
+ SCSS
299
+ div { width: 20; }
300
+ CSS
301
+ end
302
+
303
+ def test_concurrency
304
+ skip 'ProtocolError: Bad state: Future already completed'
305
+ 10.times do
306
+ threads = []
307
+ 2.times do |i|
308
+ threads << Thread.new(i) do |id|
309
+ output = @compiler.render({
310
+ data: "div { url: test-function() }",
311
+ functions: {
312
+ 'test_function()': lambda {
313
+ Sass::EmbeddedProtocol::Value.new(
314
+ :string => Sass::EmbeddedProtocol::Value::String.new(
315
+ :text => "{test_key1: 'test_value', test_key2: #{id}}",
316
+ :quoted => true
317
+ )
318
+ )
319
+ }
320
+ }
321
+ })[:css]
322
+ assert_match /test_key1/, output
323
+ assert_match /test_key2/, output
324
+ assert_match /test_value/, output
325
+ assert_match /#{id}/, output
326
+ end
327
+ end
328
+ threads.each(&:join)
329
+ end
330
+ end
331
+
332
+ def test_pass_custom_functions_as_a_parameter
333
+ output = @compiler.render({
334
+ data: "div { url: test-function(); }",
335
+ functions: {
336
+ 'test_function()': lambda {
337
+ Sass::EmbeddedProtocol::Value.new(
338
+ :string => Sass::EmbeddedProtocol::Value::String.new(
339
+ :text => "custom_function",
340
+ :quoted => true
341
+ )
342
+ )
343
+ }
344
+ }
345
+ })[:css]
346
+
347
+ assert_match /custom_function/, output
348
+ end
349
+
350
+ def test_pass_incompatible_type_to_custom_functions
351
+ assert_raises(CompilationError) do
352
+ output = @compiler.render({
353
+ data: "div { url: test-function(); }",
354
+ functions: {
355
+ 'test_function()': lambda {
356
+ Class.new
357
+ }
358
+ }
359
+ })[:css]
360
+ end
361
+ end
362
+
363
+ private
364
+
365
+ def assert_sass(sass, expected_css)
366
+ output = render(sass)
367
+ assert_equal expected_css.strip.gsub!(/\s+/, " "), # poor man's String#squish
368
+ output.strip.gsub!(/\s+/, " ")
369
+ end
370
+
371
+ def stderr_output
372
+ $stderr.string.gsub("\u0000\n", '').chomp
373
+ end
374
+ end
375
+ end