www_app 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +36 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +84 -0
- data/TODO.md +13 -0
- data/VERSION +1 -0
- data/bin/www_app +46 -0
- data/doc/Design.md +123 -0
- data/doc/Why_this_arch.rb +104 -0
- data/lib/public/jquery-2.1.1.js +4 -0
- data/lib/public/jquery.serialize-object.min.js +8 -0
- data/lib/public/underscore-1.7.0.js +6 -0
- data/lib/public/underscore-min.map +1 -0
- data/lib/public/underscore.string-2.3.0.js +1 -0
- data/lib/public/www_app.js +824 -0
- data/lib/www_app/Clean.rb +169 -0
- data/lib/www_app/dsl.rb +86 -0
- data/lib/www_app/source.rb +53 -0
- data/lib/www_app.rb +1024 -0
- data/specs/as_ruby/0000-new.rb +23 -0
- data/specs/as_ruby/0010-attrs.rb +29 -0
- data/specs/as_ruby/0011-class.rb +39 -0
- data/specs/as_ruby/0011-href.rb +37 -0
- data/specs/as_ruby/0011-id.rb +39 -0
- data/specs/as_ruby/0020-tag.rb +21 -0
- data/specs/as_ruby/0020-tag_content.rb +43 -0
- data/specs/as_ruby/0021-body.rb +16 -0
- data/specs/as_ruby/0021-form.rb +22 -0
- data/specs/as_ruby/0021-link.rb +26 -0
- data/specs/as_ruby/0021-script.rb +44 -0
- data/specs/as_ruby/0030-mustache.rb +113 -0
- data/specs/as_ruby/0040-css.rb +174 -0
- data/specs/as_ruby/0050-on.rb +64 -0
- data/specs/client-side/index.html +90 -0
- data/specs/client-side/index.js +777 -0
- data/specs/lib/config.ru +96 -0
- data/specs/lib/helpers.rb +230 -0
- data/specs/lib/qunit/qunit-1.15.0.css +237 -0
- data/specs/lib/qunit/qunit-1.15.0.js +2495 -0
- data/specs/lib/sample.rb +23 -0
- data/specs/sampe.2.rb +14 -0
- data/specs/sample.3.rb +17 -0
- data/specs/sample.rb +44 -0
- data/www_app.gemspec +38 -0
- metadata +271 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
describe "IS_DEV" do
|
4
|
+
|
5
|
+
before {
|
6
|
+
@orig = ENV['IS_DEV']
|
7
|
+
}
|
8
|
+
|
9
|
+
after {
|
10
|
+
ENV['IS_DEV'] = @orig
|
11
|
+
}
|
12
|
+
|
13
|
+
it "raises RuntimeError if passed a block and non-IS_DEV" do
|
14
|
+
should.raise(RuntimeError) {
|
15
|
+
ENV['IS_DEV'] = nil
|
16
|
+
WWW_App.new {
|
17
|
+
div {}
|
18
|
+
}
|
19
|
+
}.message.should.match /non-DEV/i
|
20
|
+
end
|
21
|
+
|
22
|
+
end # === describe IS_DEV ===
|
23
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
describe "all attrs" do
|
3
|
+
|
4
|
+
it "raises a RuntimeError if tag has an unknown attribute" do
|
5
|
+
should.raise(RuntimeError) {
|
6
|
+
actual {
|
7
|
+
a.href('/href') {
|
8
|
+
tag![:attrs][:hi] = 'hiya'
|
9
|
+
"here"
|
10
|
+
}
|
11
|
+
}
|
12
|
+
}.message.should.match /Unknown attr: :hi/
|
13
|
+
end
|
14
|
+
|
15
|
+
it "escapes attributes" do
|
16
|
+
target %^<a rel="<hello">hello</a>^
|
17
|
+
actual {
|
18
|
+
a.rel('<hello') { 'hello' }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
end # === describe all attrs
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
describe :^ do
|
5
|
+
|
6
|
+
it "raises Invalid if class has invalid chars" do
|
7
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
8
|
+
actual {
|
9
|
+
div.^('hi)o') { 'hello' }
|
10
|
+
}
|
11
|
+
}.message.should.match /hi\)o/
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# ==========================================================================
|
16
|
+
# =========== end sanitization specs =====================================
|
17
|
+
# ==========================================================================
|
18
|
+
|
19
|
+
|
20
|
+
it "adds 'class' attribute: a.^(:warning, :red) { }" do
|
21
|
+
target '<a class="warning red" href="/here">Here</a>'
|
22
|
+
|
23
|
+
actual do
|
24
|
+
a.^(:warning, :red).href('/here') { "Here" }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "merges classes: a.^(:super).^(:low)" do
|
29
|
+
target '<a class="super low" href="/now">Now</a>'
|
30
|
+
|
31
|
+
actual do
|
32
|
+
a.^(:super).^(:low).href("/now") { "Now" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end # === describe :^
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
describe :href do
|
4
|
+
|
5
|
+
it "produces 'a' elements with 'href'" do
|
6
|
+
target '<a href="/here">Here</a>'
|
7
|
+
|
8
|
+
actual do
|
9
|
+
a.href('/here') { "Here" }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "escapes chars in 'href' attributes as a url" do
|
14
|
+
target %^<a href="/home/?a&b">home</a>^
|
15
|
+
|
16
|
+
actual do
|
17
|
+
a.href('/home/?a&b') { "home" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises Invalid_HREF for :href: javacript:" do
|
22
|
+
should.raise(Escape_Escape_Escape::Invalid_HREF) {
|
23
|
+
actual do
|
24
|
+
a.href('javascript://alert()') { 'hello' }
|
25
|
+
end
|
26
|
+
}.message.should.match /javascript/
|
27
|
+
end
|
28
|
+
|
29
|
+
it "raises Escape_Escape_Escape::Invalid_Relative_HREF if not relative using :link" do
|
30
|
+
should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
|
31
|
+
actual {
|
32
|
+
link.href('http://www.google.com/s.css')./
|
33
|
+
}
|
34
|
+
}.message.should.match /google/
|
35
|
+
end
|
36
|
+
|
37
|
+
end # === describe :href
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
describe :* do
|
5
|
+
|
6
|
+
it "raises Invalid if id has invalid chars" do
|
7
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
8
|
+
actual do
|
9
|
+
div.*('a<&a') { 'hello' }
|
10
|
+
end
|
11
|
+
}.message.should.match /a<&a/
|
12
|
+
end
|
13
|
+
|
14
|
+
it "raises HTML_ID_Duplicate if id is used more than once" do
|
15
|
+
should.raise(WWW_App::HTML_ID_Duplicate) {
|
16
|
+
actual do
|
17
|
+
div.*(:my_id) { '1' }
|
18
|
+
div.*(:my_id) { '2' }
|
19
|
+
end
|
20
|
+
}.message.should.match /my_id/
|
21
|
+
end
|
22
|
+
|
23
|
+
# ==========================================================================
|
24
|
+
# =========== end sanitization specs =====================================
|
25
|
+
# ==========================================================================
|
26
|
+
|
27
|
+
|
28
|
+
it "adds 'id' attribute: a.*(:warning)(...) { }" do
|
29
|
+
target '<a id="warning" href="/there">There</a>'
|
30
|
+
|
31
|
+
actual do
|
32
|
+
a.*(:warning).href('/there') { "There" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end # === describe WWW_App.new ===
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
describe :tag do
|
3
|
+
|
4
|
+
it "raises Invalid if tag starts w/ a number" do
|
5
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
6
|
+
actual do
|
7
|
+
div.*('0a') { 'hello' }
|
8
|
+
end
|
9
|
+
}.message.should.match /0a/
|
10
|
+
end
|
11
|
+
|
12
|
+
it "raises Invalid if tag is unknown: e.g. :footers" do
|
13
|
+
should.raise(StandardError) {
|
14
|
+
actual do
|
15
|
+
tag(:footers) { 'bye' }
|
16
|
+
end
|
17
|
+
}.message.should.match /footers/
|
18
|
+
end
|
19
|
+
|
20
|
+
end # === describe :tag ===
|
21
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
describe "HTML contents" do
|
3
|
+
|
4
|
+
it "uses last String value as content" do
|
5
|
+
target %^<p>Hello</p>^
|
6
|
+
|
7
|
+
actual do
|
8
|
+
p { "Hello" }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "closes tag with :/" do
|
13
|
+
target %^<p></p>^
|
14
|
+
actual {
|
15
|
+
p./
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "does not give end-tags to void tags (self-closing tags)" do
|
20
|
+
target %^<br />\n<img />^
|
21
|
+
actual {
|
22
|
+
br./
|
23
|
+
img./
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "escapes inner text" do
|
28
|
+
target %^<p>& here lies jack</p>^
|
29
|
+
|
30
|
+
actual do
|
31
|
+
p { "& here lies jack" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "strips out W3C unallowed Unicode chars" do
|
36
|
+
target %^<div>hello hello</div>^
|
37
|
+
actual do
|
38
|
+
div { "hello \u0340\u0341\u17a3\u17d3\u2028\u2029 hello" }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end # === describe HTML contents
|
43
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
describe :forms do
|
3
|
+
|
4
|
+
it "requires a CSRF token" do
|
5
|
+
should.raise(Mustache::ContextMiss) {
|
6
|
+
actual do
|
7
|
+
form.action('/home') {
|
8
|
+
}
|
9
|
+
end
|
10
|
+
}.message.should.match /auth_token/
|
11
|
+
end
|
12
|
+
|
13
|
+
it "raises Invalid_Relative_HREF if :action is not a relative url" do
|
14
|
+
should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
|
15
|
+
actual :auth_token => 'mytoken' do
|
16
|
+
form.action('http://www.google.com/') {}
|
17
|
+
end
|
18
|
+
}.message.should.match /google.com/
|
19
|
+
end
|
20
|
+
|
21
|
+
end # === describe :forms ===
|
22
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
describe :link do
|
3
|
+
|
4
|
+
it "raises Invalid_Relative_HREF" do
|
5
|
+
should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
|
6
|
+
actual do
|
7
|
+
link.href('http://www.google.com/')./
|
8
|
+
end
|
9
|
+
}.message.should.match /google/
|
10
|
+
end
|
11
|
+
|
12
|
+
it "escapes slashes in :href" do
|
13
|
+
target %^<link href="/css/css/styles.css" />^
|
14
|
+
actual {
|
15
|
+
link.href('/css/css/styles.css')./
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "allows a relative :href" do
|
20
|
+
target %^<link href="/css.css" />^
|
21
|
+
actual {
|
22
|
+
link.href('/css.css')./
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end # === describe :link
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
describe :script do
|
3
|
+
|
4
|
+
it "raises Invalid_Relative_HREF if :src is not relative" do
|
5
|
+
should.raise(Escape_Escape_Escape::Invalid_Relative_HREF) {
|
6
|
+
actual do
|
7
|
+
script.src('http://www.example.org/file.js')./
|
8
|
+
end
|
9
|
+
}.message.should.match /example\.org/
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows a relative :src" do
|
13
|
+
target %^<script src="/file.js"></script>^
|
14
|
+
actual {
|
15
|
+
script.src('/file.js')./
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "escapes slashes in attr :src" do
|
20
|
+
target %^<script src="/dir/file.js"></script>^
|
21
|
+
actual {
|
22
|
+
script.src('/dir/file.js')./
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not allow vars in :script :src attribute" do
|
27
|
+
target :body, <<-EOF
|
28
|
+
<script src="help"></script>
|
29
|
+
EOF
|
30
|
+
|
31
|
+
actual do
|
32
|
+
script.src(:help)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "ignores text for :script block" do
|
37
|
+
target %^<script src="/hello.js"></script>^
|
38
|
+
actual {
|
39
|
+
script.src('/hello.js') { 'hello' }
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end # === describe :JS ===
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
describe :mustache do
|
3
|
+
|
4
|
+
it "replaces values with hash" do
|
5
|
+
target <<-EOF
|
6
|
+
<div>hello</div>
|
7
|
+
EOF
|
8
|
+
|
9
|
+
actual greeting: 'hello' do
|
10
|
+
div { :greeting }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "raises ContextMiss for unauthorized methods" do
|
15
|
+
should.raise(Mustache::ContextMiss) {
|
16
|
+
target "nothing"
|
17
|
+
actual name: 'Bob' do
|
18
|
+
div { :object_id }
|
19
|
+
end
|
20
|
+
}.message.should.match /Can't find \.html\(:object_id\)/i
|
21
|
+
|
22
|
+
should.raise(Mustache::ContextMiss) {
|
23
|
+
target "nothing"
|
24
|
+
actual name: 'Bob' do
|
25
|
+
div { :to_s }
|
26
|
+
end
|
27
|
+
}.message.should.match /Can't find \.html\(:to_s\)/i
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises ContextMiss when an unknown value is requested" do
|
31
|
+
should.raise(Mustache::ContextMiss) {
|
32
|
+
actual name: 'Bob' do
|
33
|
+
div { :object_ids }
|
34
|
+
end
|
35
|
+
}.message.should.match /object_ids/
|
36
|
+
end
|
37
|
+
|
38
|
+
it "renders if inverted section and key is not defined" do
|
39
|
+
target <<-EOF
|
40
|
+
<div>hello</div>
|
41
|
+
EOF
|
42
|
+
|
43
|
+
actual {
|
44
|
+
render_unless(:here) { div { 'hello' } }
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "escapes html" do
|
49
|
+
target <<-EOF
|
50
|
+
<div>& / hello</div>
|
51
|
+
EOF
|
52
|
+
|
53
|
+
actual :hello=>'& / hello' do
|
54
|
+
div { :hello }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "escapes html in nested values" do
|
59
|
+
target <<-EOF
|
60
|
+
<div id="my_box"><span>& hello 1</span>
|
61
|
+
<span>&& hello 2</span></div>
|
62
|
+
EOF
|
63
|
+
|
64
|
+
actual(:hello=>{:msg=>'& hello 1', :goodbye=>nil}) {
|
65
|
+
div.*(:my_box) {
|
66
|
+
render_if(:hello) {
|
67
|
+
span { :msg }
|
68
|
+
render_unless(:goodbye) { span { '&& hello 2' } }
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it "escapes :href in nested values" do
|
75
|
+
target %^<div><div><a href="/hello">hello</a></div></div>^
|
76
|
+
actual(o: {url: '/hello', msg: 'hello'}) {
|
77
|
+
div {
|
78
|
+
render_if(:o) {
|
79
|
+
div { a.href(:url) { :msg } }
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not allow vars to be used in form :action" do
|
86
|
+
target %^<form action="url"><input type="hidden" name="auth_token" value="hello" /></form>^
|
87
|
+
|
88
|
+
actual :auth_token => 'hello' do
|
89
|
+
form.action(:url) {}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "does not allow vars to be used in :link :href" do
|
94
|
+
target %^<link href="hello" />^
|
95
|
+
actual {
|
96
|
+
link.href(:hello)./
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises ContextMiss if encounters unescaped value" do
|
101
|
+
should.raise(Mustache::ContextMiss) {
|
102
|
+
actual(blue: 'hello<') {
|
103
|
+
div { '-- {{{ blue }}}' }
|
104
|
+
}
|
105
|
+
}.message.should.match /blue/
|
106
|
+
end
|
107
|
+
|
108
|
+
end # === describe Sanitize mustache ===
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
@@ -0,0 +1,174 @@
|
|
1
|
+
|
2
|
+
describe :css do
|
3
|
+
|
4
|
+
it "raises Invalid if class name has invalid chars: class=\"hi;o\"" do
|
5
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
6
|
+
actual {
|
7
|
+
div.^('hi;o') {
|
8
|
+
border '1px solid #fff'
|
9
|
+
'hi o'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
}.message.should.match /hi;o/
|
13
|
+
end
|
14
|
+
|
15
|
+
it "raises Invalid if css value has invalid chars: something *" do
|
16
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
17
|
+
actual do
|
18
|
+
div {
|
19
|
+
background 'something *'
|
20
|
+
}
|
21
|
+
end
|
22
|
+
}.message.should.match /something \*/
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raises Invalid if css selector has invalid chars: s*s' do
|
26
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
27
|
+
actual do
|
28
|
+
div.^("s*s") { border '1px'}
|
29
|
+
end
|
30
|
+
}.message.should.match /s\*s/
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raises Invalid if contains 'expression:'" do
|
34
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
35
|
+
actual do
|
36
|
+
div {
|
37
|
+
background 'solid expression:'
|
38
|
+
}
|
39
|
+
end
|
40
|
+
}.message.should.match /expression:/
|
41
|
+
end
|
42
|
+
|
43
|
+
it "raises Invalid if contains 'expression&'" do
|
44
|
+
should.raise(Escape_Escape_Escape::Invalid) {
|
45
|
+
actual do
|
46
|
+
div {
|
47
|
+
background 'solid expression&'
|
48
|
+
}
|
49
|
+
end
|
50
|
+
}.message.should.match /expression&/
|
51
|
+
end
|
52
|
+
|
53
|
+
it "sanitizes urls" do
|
54
|
+
target :style, <<-EOF
|
55
|
+
div.box {
|
56
|
+
background-image: url(http://www.example.com/back.png);
|
57
|
+
}
|
58
|
+
EOF
|
59
|
+
|
60
|
+
actual do
|
61
|
+
div.^(:box) {
|
62
|
+
background_image 'http://www.example.com/back.png'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# ==========================================================================
|
69
|
+
# =========== end sanitization specs =====================================
|
70
|
+
# ==========================================================================
|
71
|
+
|
72
|
+
|
73
|
+
it 'allows css selectors with valid chars: #my_box div.box' do
|
74
|
+
target :style, <<-EOF
|
75
|
+
#my_box div.box {
|
76
|
+
border: 1px;
|
77
|
+
}
|
78
|
+
EOF
|
79
|
+
|
80
|
+
actual {
|
81
|
+
div.*(:my_box) {
|
82
|
+
div.^(:box) { border '1px' }
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "adds a 'style' tag to 'head'" do
|
88
|
+
target :outer, :style, %^
|
89
|
+
<style type="text/css">
|
90
|
+
#the_box {
|
91
|
+
border-width: 10px;
|
92
|
+
}
|
93
|
+
</style>
|
94
|
+
^
|
95
|
+
|
96
|
+
actual do
|
97
|
+
div.*(:the_box) {
|
98
|
+
border_width '10px'
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "uses id of element to add style" do
|
104
|
+
target :style, %^
|
105
|
+
#my_box {
|
106
|
+
border-width: 1px;
|
107
|
+
}
|
108
|
+
^
|
109
|
+
|
110
|
+
actual do
|
111
|
+
div.*(:my_box) {
|
112
|
+
border_width '1px'
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "uses tag hierarchy if no id found" do
|
118
|
+
target :style, %^
|
119
|
+
div div span {
|
120
|
+
width: 20px;
|
121
|
+
}
|
122
|
+
^
|
123
|
+
|
124
|
+
actual do
|
125
|
+
div { div { span { width '20px' } } }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it "does not include parents when element has id" do
|
130
|
+
target :style, <<-EOF
|
131
|
+
#my_box div.box {
|
132
|
+
border: 15px;
|
133
|
+
}
|
134
|
+
EOF
|
135
|
+
|
136
|
+
actual do
|
137
|
+
div.^(:top) {
|
138
|
+
div.*(:my_box) {
|
139
|
+
div.^(:box) { border '15px' }
|
140
|
+
}
|
141
|
+
}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
it "does not accept vars for css values" do
|
147
|
+
target :style, %^
|
148
|
+
div {
|
149
|
+
border: something;
|
150
|
+
}
|
151
|
+
^
|
152
|
+
actual {
|
153
|
+
div {
|
154
|
+
border :something
|
155
|
+
}
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "does not accept vars for css -image values" do
|
160
|
+
target :style, %^
|
161
|
+
div {
|
162
|
+
background-image: url(something);
|
163
|
+
}
|
164
|
+
^
|
165
|
+
actual {
|
166
|
+
div {
|
167
|
+
background_image :something
|
168
|
+
}
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
end # === sanitize css selectors
|
173
|
+
|
174
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
describe "HTML :on" do
|
3
|
+
|
4
|
+
it "escapes text as :html" do
|
5
|
+
target :script, <<-EOF
|
6
|
+
WWW_App.compile(
|
7
|
+
["create_event",["div","click","add_class",["red<red"]]]
|
8
|
+
);
|
9
|
+
EOF
|
10
|
+
|
11
|
+
actual do
|
12
|
+
div {
|
13
|
+
on(:click) { add_class "red<red" }
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "renders js" do
|
19
|
+
target :script, <<-EOF
|
20
|
+
WWW_App.compile(
|
21
|
+
#{
|
22
|
+
Escape_Escape_Escape.json_encode( ["create_event", [ "#my_box", "click", "add_class", ["hello"] ] ] )
|
23
|
+
}
|
24
|
+
);
|
25
|
+
EOF
|
26
|
+
|
27
|
+
actual {
|
28
|
+
div.*(:my_box) {
|
29
|
+
on(:click) { add_class :hello }
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adds class to id of element: #id.class" do
|
35
|
+
target :style, %^
|
36
|
+
#me.highlight {
|
37
|
+
border-color: #fff;
|
38
|
+
}
|
39
|
+
^
|
40
|
+
|
41
|
+
actual do
|
42
|
+
div.*(:me) {
|
43
|
+
on(:highlight) { border_color '#fff' }
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "adds a psuedo class if passed a String" do
|
49
|
+
target :style, <<-EOF
|
50
|
+
a:hover {
|
51
|
+
border: 12px;
|
52
|
+
}
|
53
|
+
EOF
|
54
|
+
|
55
|
+
actual {
|
56
|
+
a.href('/home') {
|
57
|
+
on(':hover') { border '12px' }
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end # === describe
|
63
|
+
|
64
|
+
|