skim 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/test/helper.rb ADDED
@@ -0,0 +1,92 @@
1
+ require "rubygems"
2
+ require "minitest/unit"
3
+ require "minitest/reporters"
4
+ require "skim"
5
+ require "coffee_script"
6
+
7
+ MiniTest::Unit.runner = MiniTest::SuiteRunner.new
8
+ MiniTest::Unit.runner.reporters <<
9
+ if ENV["RM_INFO"] || ENV["TEAMCITY_VERSION"]
10
+ MiniTest::Reporters::RubyMineReporter.new
11
+ else
12
+ MiniTest::Reporters::ProgressReporter.new
13
+ end
14
+
15
+ MiniTest::Unit.autorun
16
+
17
+ class TestSkim < MiniTest::Unit::TestCase
18
+ def context_source
19
+ File.read(File.expand_path("../context.coffee", __FILE__))
20
+ end
21
+
22
+ def setup
23
+ @context = CoffeeScript.compile(context_source, :bare => true)
24
+ end
25
+
26
+ def context(options)
27
+ case context = options[:context]
28
+ when String
29
+ context
30
+ when Hash
31
+ MultiJson.encode(context)
32
+ else
33
+ "new Context()"
34
+ end
35
+ end
36
+
37
+ def compile(source, options = {})
38
+ Skim::Template.new(options[:file], options) { source }.render(options[:scope] || Env.new)
39
+ end
40
+
41
+ def evaluate(source, options = {})
42
+ require "execjs"
43
+ code = [
44
+ @context,
45
+ "var context = #{context(options)}",
46
+ "var template = #{compile(source, options)}",
47
+ "var evaluate = function () { return template(context); }"
48
+ ]
49
+ if Skim::Engine.default_options[:use_asset]
50
+ code.unshift CoffeeScript.compile(Skim::Template.skim_src)
51
+ end
52
+ context = ExecJS.compile(code.join(";"))
53
+ context.call("evaluate")
54
+ end
55
+
56
+ def render(source, options = {})
57
+ evaluate(source, options)
58
+ end
59
+
60
+ def assert_html(expected, source, options = {}, &block)
61
+ assert_equal expected, render(source, options, &block)
62
+ end
63
+
64
+ def with_and_without_asset
65
+ yield
66
+
67
+ begin
68
+ Skim::Engine.default_options[:use_asset] = true
69
+ yield
70
+ ensure
71
+ Skim::Engine.default_options[:use_asset] = false
72
+ end
73
+ end
74
+
75
+ def assert_runtime_error(message, source, options = {})
76
+ render(source, options)
77
+ raise Exception, 'Runtime error expected'
78
+ rescue RuntimeError => ex
79
+ assert_equal message, ex.message
80
+ end
81
+ end
82
+
83
+ class Env
84
+ def hello_world(text = "Hello World from @env", opts = {})
85
+ text << opts.to_a * " " if opts.any?
86
+ if block_given?
87
+ "#{text} #{yield} #{text}"
88
+ else
89
+ text
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ class TestAttributeMerger < MiniTest::Unit::TestCase
4
+ def call(expr)
5
+ Temple::CoffeeScript::AttributeMerger.new.call(expr)
6
+ end
7
+
8
+ def test_passes_single_attributes
9
+ sexp = [:html, :attrs,
10
+ [:html, :attr, "foo", [:static, "a"]],
11
+ [:html, :attr, "bar", [:static, "b"]]]
12
+ assert_equal sexp, call(sexp)
13
+ end
14
+
15
+ def test_merges_multiple_attributes
16
+ sexp = [:html, :attrs,
17
+ [:html, :attr, "class", [:static, "a"]],
18
+ [:html, :attr, "class", [:static, "b"]]]
19
+ assert_equal [:html, :attrs,
20
+ [:html, :attr, "class",
21
+ [:multi, [:static, "a"], [:static, " "], [:static, "b"]]]], call(sexp)
22
+ end
23
+ end
@@ -0,0 +1,88 @@
1
+ require 'helper'
2
+
3
+ class TestSkimCodeBlocks < TestSkim
4
+ def test_render_with_output_code_block
5
+ source = %q{
6
+ p
7
+ = @callback "Hello Ruby!", ->
8
+ | Hello from within a block!
9
+ }
10
+
11
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
12
+ end
13
+
14
+ def test_render_with_output_code_within_block
15
+ source = %q{
16
+ p
17
+ = @callback "Hello Ruby!", =>
18
+ = @callback "Hello from within a block!"
19
+ }
20
+
21
+ assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
22
+ end
23
+
24
+ def test_render_with_output_code_within_block_2
25
+ source = %q{
26
+ p
27
+ = @callback "Hello Ruby!", =>
28
+ = @callback "Hello from within a block!", =>
29
+ = @callback "And another one!"
30
+ }
31
+
32
+ assert_html '<p>Hello Ruby! Hello from within a block! And another one! Hello from within a block! Hello Ruby!</p>', source
33
+ end
34
+
35
+ def test_output_block_with_arguments
36
+ source = %q{
37
+ p
38
+ = @define_macro 'person', (first_name, last_name) =>
39
+ .first_name = first_name
40
+ .last_name = last_name
41
+ == @call_macro 'person', 'John', 'Doe'
42
+ == @call_macro 'person', 'Max', 'Mustermann'
43
+ }
44
+
45
+ assert_html '<p><div class="first_name">John</div><div class="last_name">Doe</div><div class="first_name">Max</div><div class="last_name">Mustermann</div></p>', source
46
+ end
47
+
48
+
49
+ def test_render_with_control_code_forEach_loop
50
+ source = %q{
51
+ p
52
+ - [0..2].forEach =>
53
+ | Hey!
54
+ }
55
+
56
+ assert_html '<p>Hey!Hey!Hey!</p>', source
57
+ end
58
+
59
+ def test_render_with_control_code_for_in_loop
60
+ source = %q{
61
+ p
62
+ - for i in [0..2]
63
+ | Hey!
64
+ }
65
+
66
+ assert_html '<p>Hey!Hey!Hey!</p>', source
67
+ end
68
+
69
+ def test_render_with_control_code_for_own_of_loop
70
+ source = %q{
71
+ p
72
+ - for own key, value of {user: 'name'}
73
+ | #{key} #{value}
74
+ }
75
+
76
+ assert_html '<p>user name</p>', source
77
+ end
78
+
79
+ def test_captured_code_block_with_conditional
80
+ source = %q{
81
+ = @callback "Hello Ruby!", ->
82
+ - if true
83
+ | Hello from within a block!
84
+ }
85
+
86
+ assert_html 'Hello Ruby! Hello from within a block! Hello Ruby!', source
87
+ end
88
+ end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+
3
+ class TestSkimCodeEscaping < TestSkim
4
+ def test_escaping_evil_method
5
+ source = %q{
6
+ p = @evil_method()
7
+ }
8
+
9
+ assert_html '<p>&lt;script&gt;do_something_evil();&lt;&#47;script&gt;</p>', source
10
+ end
11
+
12
+ def test_render_unsafe
13
+ source = %q{
14
+ p = "<strong>Hello World\\n, meet \\"Skim\\"</strong>."
15
+ }
16
+
17
+ assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Skim\&quot;&lt;&#47;strong&gt;.</p>", source
18
+ end
19
+
20
+ def test_render_safe
21
+ source = %q{
22
+ p = @safe("<strong>Hello World\\n, meet \\"Skim\\"</strong>.")
23
+ }
24
+
25
+ assert_html "<p><strong>Hello World\n, meet \"Skim\"</strong>.</p>", source
26
+ end
27
+
28
+ def test_render_with_disable_escape_false
29
+ source = %q{
30
+ = "<p>Hello</p>"
31
+ == "<p>World</p>"
32
+ }
33
+
34
+ assert_html "&lt;p&gt;Hello&lt;&#47;p&gt;<p>World</p>", source
35
+ end
36
+
37
+ def test_render_with_disable_escape_true
38
+ source = %q{
39
+ = "<p>Hello</p>"
40
+ == "<p>World</p>"
41
+ }
42
+
43
+ assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true
44
+ end
45
+
46
+ def test_explicit_escape
47
+ source = %q{
48
+ = @escape "Crate & Barrel"
49
+ }
50
+
51
+ assert_html "Crate &amp; Barrel", source
52
+ end
53
+
54
+ def test_explicit_safe
55
+ source = %q{
56
+ = @safe "Crate & Barrel"
57
+ }
58
+
59
+ assert_html "Crate & Barrel", source
60
+ end
61
+
62
+ def test_user_defined_escape
63
+ source = %q{
64
+ = "Crate & Barrel"
65
+ }
66
+
67
+ assert_html "CRATE & BARREL", source, :context => "{escape: function (value) { return value.toUpperCase(); }}"
68
+ end
69
+ end
@@ -0,0 +1,285 @@
1
+ require 'helper'
2
+
3
+ class TestSkimCodeEvaluation < TestSkim
4
+ def test_render_with_call_to_set_attributes
5
+ source = %q{
6
+ p id="#{@id_helper()}" class="hello world" = @hello_world()
7
+ }
8
+
9
+ assert_html '<p class="hello world" id="notice">Hello World from @env</p>', source
10
+ end
11
+
12
+ def test_render_with_call_to_set_custom_attributes
13
+ source = %q{
14
+ p data-id="#{@id_helper()}" data-class="hello world"
15
+ = @hello_world()
16
+ }
17
+
18
+ assert_html '<p data-class="hello world" data-id="notice">Hello World from @env</p>', source
19
+ end
20
+
21
+ def test_render_with_call_to_set_attributes_and_call_to_set_content
22
+ source = %q{
23
+ p id="#{@id_helper()}" class="hello world" = @hello_world()
24
+ }
25
+
26
+ assert_html '<p class="hello world" id="notice">Hello World from @env</p>', source
27
+ end
28
+
29
+ def test_render_with_parameterized_call_to_set_attributes_and_call_to_set_content
30
+ source = %q{
31
+ p id="#{@id_helper()}" class="hello world" = @hello_world("Hello Ruby!")
32
+ }
33
+
34
+ assert_html '<p class="hello world" id="notice">Hello Ruby!</p>', source
35
+ end
36
+
37
+ def test_render_with_spaced_parameterized_call_to_set_attributes_and_call_to_set_content
38
+ source = %q{
39
+ p id="#{@id_helper()}" class="hello world" = @hello_world "Hello Ruby!"
40
+ }
41
+
42
+ assert_html '<p class="hello world" id="notice">Hello Ruby!</p>', source
43
+ end
44
+
45
+ def test_render_with_spaced_parameterized_call_to_set_attributes_and_call_to_set_content_2
46
+ source = %q{
47
+ p id="#{@id_helper()}" class="hello world" = @hello_world "Hello Ruby!", dummy: "value"
48
+ }
49
+
50
+ assert_html '<p class="hello world" id="notice">Hello Ruby!dummy value</p>', source
51
+ end
52
+
53
+ def test_hash_call_in_attribute
54
+ source = %q{
55
+ p id="#{@hash()['a']}" Test it
56
+ }
57
+
58
+ assert_html '<p id="The letter a">Test it</p>', source
59
+ end
60
+
61
+ def test_instance_variable_in_attribute_without_quotes
62
+ source = %q{
63
+ p id=@var
64
+ }
65
+
66
+ assert_html '<p id="instance"></p>', source
67
+ end
68
+
69
+ def test_method_call_in_attribute_without_quotes
70
+ source = %q{
71
+ form action=@action_path("page", "save") method='post'
72
+ }
73
+
74
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
75
+ end
76
+
77
+ def test_ruby_attribute_with_unbalanced_delimiters
78
+ source = %q{
79
+ div crazy=@action_path('[') id="crazy_delimiters"
80
+ }
81
+
82
+ assert_html '<div crazy="&#47;action-[" id="crazy_delimiters"></div>', source
83
+ end
84
+
85
+ def test_method_call_in_delimited_attribute_without_quotes
86
+ source = %q{
87
+ form(action=@action_path("page", "save") method='post')
88
+ }
89
+
90
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
91
+ end
92
+
93
+ def test_method_call_in_delimited_attribute_without_quotes2
94
+ source = %q{
95
+ form(method='post' action=@action_path("page", "save"))
96
+ }
97
+
98
+ assert_html '<form action="&#47;action-page-save" method="post"></form>', source
99
+ end
100
+
101
+ def test_bypassing_escape_in_attribute
102
+ source = %q{
103
+ form action==@action_path("page", "save") method='post'
104
+ }
105
+
106
+ assert_html '<form action="/action-page-save" method="post"></form>', source
107
+ end
108
+
109
+ def test_hash_call_in_attribute_without_quotes
110
+ source = %q{
111
+ p id=@hash()['a'] Test it
112
+ }
113
+
114
+ assert_html '<p id="The letter a">Test it</p>', source
115
+ end
116
+
117
+ def test_hash_call_in_delimited_attribute
118
+ source = %q{
119
+ p(id=@hash()['a']) Test it
120
+ }
121
+
122
+ assert_html '<p id="The letter a">Test it</p>', source
123
+ end
124
+
125
+ def test_hash_call_in_attribute_with_ruby_evaluation
126
+ source = %q{
127
+ p id={@hash()['a'] + @hash()['a']} Test it
128
+ }
129
+
130
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
131
+ end
132
+
133
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation
134
+ source = %q{
135
+ p(id=(@hash()['a'] + @hash()['a'])) Test it
136
+ }
137
+
138
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
139
+ end
140
+
141
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_2
142
+ source = %q{
143
+ p[id=(@hash()['a'] + @hash()['a'])] Test it
144
+ }
145
+
146
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
147
+ end
148
+
149
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_3
150
+ source = %q{
151
+ p(id=[@hash()['a'] + @hash()['a']]) Test it
152
+ }
153
+
154
+ assert_html '<p id="The letter aThe letter a">Test it</p>', source
155
+ end
156
+
157
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4
158
+ source = %q{
159
+ p(id=[@hash()['a'] + @hash()['a']] class=[@hash()['a']]) Test it
160
+ }
161
+
162
+ assert_html '<p class="The letter a" id="The letter aThe letter a">Test it</p>', source
163
+ end
164
+
165
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_5
166
+ source = %q{
167
+ p(id=@hash()['a'] class=[@hash()['a']]) Test it
168
+ }
169
+
170
+ assert_html '<p class="The letter a" id="The letter a">Test it</p>', source
171
+ end
172
+
173
+ def test_computation_in_attribute
174
+ source = %q{
175
+ p id=(1 + 1)*5 Test it
176
+ }
177
+
178
+ assert_html '<p id="10">Test it</p>', source
179
+ end
180
+
181
+ def test_number_type_interpolation
182
+ source = %q{
183
+ p = @output_number()
184
+ }
185
+
186
+ assert_html '<p>1337</p>', source
187
+ end
188
+
189
+ def test_ternary_operation_in_attribute
190
+ source = %q{
191
+ p id="#{(if false then 'notshown' else 'shown')}" = @output_number()
192
+ }
193
+
194
+ assert_html '<p id="shown">1337</p>', source
195
+ end
196
+
197
+ def test_class_attribute_merging
198
+ source = %{
199
+ .alpha class="beta" Test it
200
+ }
201
+ assert_html '<div class="alpha beta">Test it</div>', source
202
+ end
203
+
204
+ def test_class_attribute_merging_with_null
205
+ skip "pending"
206
+
207
+ source = %{
208
+ .alpha class="beta" class=null class="gamma" Test it
209
+ }
210
+ assert_html '<div class="alpha beta gamma">Test it</div>', source
211
+ end
212
+
213
+ def test_id_attribute_merging
214
+ source = %{
215
+ #alpha id="beta" Test it
216
+ }
217
+ assert_html '<div id="alpha_beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '_' }
218
+ end
219
+
220
+ def test_id_attribute_merging2
221
+ source = %{
222
+ #alpha id="beta" Test it
223
+ }
224
+ assert_html '<div id="alpha-beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '-' }
225
+ end
226
+
227
+ def test_boolean_attribute_false
228
+ source = %{
229
+ option selected=false Text
230
+ }
231
+
232
+ assert_html '<option>Text</option>', source
233
+ end
234
+
235
+ def test_boolean_attribute_true
236
+ source = %{
237
+ option selected=true Text
238
+ }
239
+
240
+ assert_html '<option selected="selected">Text</option>', source
241
+ end
242
+
243
+ def test_boolean_attribute_dynamic
244
+ source = %{
245
+ option selected=@method_which_returns_true() Text
246
+ }
247
+
248
+ assert_html '<option selected="selected">Text</option>', source
249
+ end
250
+
251
+ def test_boolean_attribute_null
252
+ source = %{
253
+ option selected=null Text
254
+ }
255
+
256
+ assert_html '<option>Text</option>', source
257
+ end
258
+
259
+ def test_boolean_attribute_string2
260
+ source = %{
261
+ option selected="selected" Text
262
+ }
263
+
264
+ assert_html '<option selected="selected">Text</option>', source
265
+ end
266
+
267
+ def test_boolean_attribute_shortcut
268
+ source = %{
269
+ option(class="clazz" selected) Text
270
+ option(selected class="clazz") Text
271
+ }
272
+
273
+ assert_html '<option class="clazz" selected="selected">Text</option><option class="clazz" selected="selected">Text</option>', source
274
+ end
275
+
276
+ def test_array_attribute
277
+ skip "pending"
278
+
279
+ source = %{
280
+ .alpha class="beta" class=['gamma', null, 'delta', [true, false]]
281
+ }
282
+
283
+ assert_html '<div class="alpha beta gamma delta true false"></div>', source
284
+ end
285
+ end
@@ -0,0 +1,143 @@
1
+ require 'helper'
2
+
3
+ class TestSkimCodeOutput < TestSkim
4
+ def test_render_with_call
5
+ source = %q{
6
+ p
7
+ = @hello_world()
8
+ }
9
+
10
+ assert_html '<p>Hello World from @env</p>', source
11
+ end
12
+
13
+ def test_render_with_trailing_whitespace
14
+ source = %q{
15
+ p
16
+ =' @hello_world()
17
+ }
18
+
19
+ assert_html '<p>Hello World from @env </p>', source
20
+ end
21
+
22
+ def test_render_with_trailing_whitespace_after_tag
23
+ source = %q{
24
+ p=' @hello_world()
25
+ }
26
+
27
+ assert_html '<p>Hello World from @env</p> ', source
28
+ end
29
+
30
+ def test_no_escape_render_with_trailing_whitespace
31
+ source = %q{
32
+ p
33
+ ==' @hello_world()
34
+ }
35
+
36
+ assert_html '<p>Hello World from @env </p>', source
37
+ end
38
+
39
+ def test_no_escape_render_with_trailing_whitespace_after_tag
40
+ source = %q{
41
+ p==' @hello_world()
42
+ }
43
+
44
+ assert_html '<p>Hello World from @env</p> ', source
45
+ end
46
+
47
+ def test_render_with_conditional_call
48
+ source = %q{
49
+ p
50
+ = @hello_world() if true
51
+ }
52
+
53
+ assert_html '<p>Hello World from @env</p>', source
54
+ end
55
+
56
+ def test_render_with_parameterized_call
57
+ source = %q{
58
+ p
59
+ = @hello_world("Hello Ruby!")
60
+ }
61
+
62
+ assert_html '<p>Hello Ruby!</p>', source
63
+ end
64
+
65
+ def test_render_with_spaced_parameterized_call
66
+ source = %q{
67
+ p
68
+ = @hello_world "Hello Ruby!"
69
+ }
70
+
71
+ assert_html '<p>Hello Ruby!</p>', source
72
+ end
73
+
74
+ def test_render_with_spaced_parameterized_call_2
75
+ source = %q{
76
+ p
77
+ = @hello_world "Hello Ruby!", dummy: "value"
78
+ }
79
+
80
+ assert_html '<p>Hello Ruby!dummy value</p>', source
81
+ end
82
+
83
+ def test_render_with_call_and_inline_text
84
+ source = %q{
85
+ h1 This is my title
86
+ p
87
+ = @hello_world()
88
+ }
89
+
90
+ assert_html '<h1>This is my title</h1><p>Hello World from @env</p>', source
91
+ end
92
+
93
+ def test_render_with_attribute_starts_with_keyword
94
+ source = %q{
95
+ p = @hello_world @in_keyword()
96
+ }
97
+
98
+ assert_html '<p>starts with keyword</p>', source
99
+ end
100
+
101
+ def test_hash_call
102
+ source = %q{
103
+ p = @hash()["a"]
104
+ }
105
+
106
+ assert_html '<p>The letter a</p>', source
107
+ end
108
+
109
+ def test_tag_output_without_space
110
+ source = %q{
111
+ p= @hello_world()
112
+ p=@hello_world()
113
+ }
114
+
115
+ assert_html '<p>Hello World from @env</p><p>Hello World from @env</p>', source
116
+ end
117
+
118
+ def test_class_output_without_space
119
+ source = %q{
120
+ .test=@hello_world()
121
+ #test==@hello_world()
122
+ }
123
+
124
+ assert_html '<div class="test">Hello World from @env</div><div id="test">Hello World from @env</div>', source
125
+ end
126
+
127
+ def test_attribute_output_without_space
128
+ source = %q{
129
+ p id="test"=@hello_world()
130
+ p(id="test")==@hello_world()
131
+ }
132
+
133
+ assert_html '<p id="test">Hello World from @env</p><p id="test">Hello World from @env</p>', source
134
+ end
135
+
136
+ def test_render_with_no_trailing_character
137
+ source = %q{
138
+ p
139
+ = @hello_world()}
140
+
141
+ assert_html '<p>Hello World from @env</p>', source
142
+ end
143
+ end