1 | // Copyright (C) 2009 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package com.google.caja.plugin.templates; |
16 | |
17 | import com.google.caja.lexer.ParseException; |
18 | import com.google.caja.parser.js.Expression; |
19 | import com.google.caja.parser.js.Operation; |
20 | import com.google.caja.parser.js.Operator; |
21 | import com.google.caja.util.CajaTestCase; |
22 | |
23 | public class JsConcatenatorTest extends CajaTestCase { |
24 | public final void testEmptyAppender() throws ParseException { |
25 | assertConcatenate(" '' "); |
26 | } |
27 | |
28 | public final void testLiterals() throws ParseException { |
29 | assertConcatenate(" 'foo' ", " 'foo' "); |
30 | assertConcatenate(" '3' ", "3"); |
31 | assertConcatenate(" '-1' ", "-1"); |
32 | assertConcatenate(" '100000000000000000000' ", "1e20"); |
33 | assertConcatenate(" '1e+21' ", "1e21"); |
34 | assertConcatenate(" '-1e+21' ", "-1e21"); |
35 | assertConcatenate(" '0' ", "0"); |
36 | assertConcatenate(" 'true' ", "true"); |
37 | assertConcatenate(" 'false' ", "false"); |
38 | assertConcatenate(" 'null' ", "null"); |
39 | } |
40 | |
41 | public final void testMoreSingleAppends() throws ParseException { |
42 | assertConcatenate(" a ? 'foo' : 'bar' ", " a ? 'foo' : 'bar' "); |
43 | assertConcatenate(" rnd() ? '' + new Date : '' ", "rnd() ? new Date : '' "); |
44 | assertConcatenate(" '' + (x || 'foo') ", " x || 'foo' "); |
45 | assertConcatenate(" '' + ['foo'] ", " ['foo'] "); |
46 | assertConcatenate(" x = f(), '' + x * x ", " x = f(), x * x "); |
47 | assertConcatenate(" x = f(), '' + x * x ", " x = f(), '' + x * x "); |
48 | } |
49 | |
50 | public final void testDecomposing() throws ParseException { |
51 | assertConcatenate(" a + 'b' + c ", "a", "'b'", "c"); |
52 | assertConcatenate(" a + 'b' + c ", "a + 'b'", "c"); |
53 | assertConcatenate(" a + 'b' + c ", "a", "'b' + c"); |
54 | assertConcatenate(" a + 'b' + c ", "a + ('b' + c)"); |
55 | assertConcatenate(" a + 'b' + c ", "(a + 'b') + c"); |
56 | assertConcatenate(" a + 'b' + c ", " '' + a + 'b' + c"); |
57 | } |
58 | |
59 | public final void testNumericAdditionUnchanged() throws ParseException { |
60 | assertConcatenate(" '' + (a + b + c) ", " a + b + c "); |
61 | assertConcatenate(" '' + a + b + c ", "a", "b", "c"); |
62 | } |
63 | |
64 | public final void testCollapsing() throws ParseException { |
65 | assertConcatenate(" 'Hello, ' + world + '!' ", |
66 | " 'Hello' ", " ', ' ", "world", " '!' "); |
67 | } |
68 | |
69 | public final void testStringForcing() throws ParseException { |
70 | assertConcatenate( |
71 | "(rand() ? a : 'b') + 'foo' + bar", |
72 | " rand() ? a : 'b' ", " 'foo' ", "bar"); |
73 | assertConcatenate( |
74 | "(rand() ? '' + a : 'b') + foo + bar", |
75 | " rand() ? a : 'b' ", "foo", "bar"); |
76 | // The (new) operator always returns an object which will cause the (+) |
77 | // operator to return a string. |
78 | assertConcatenate( |
79 | "new Date(year, month, day) + message", |
80 | "new Date(year, month, day)", "message"); |
81 | // But the (new) operator by itself does not always return a string, so in |
82 | // the single operand case, we do need to force it to a string. |
83 | assertConcatenate( |
84 | "'' + new Date(year, month, day)", |
85 | "new Date(year, month, day)"); |
86 | assertConcatenate( // Coerces to a number. |
87 | " '' + (+(new Date)) ", "+(new Date)"); |
88 | } |
89 | |
90 | public final void testSideEffects() throws ParseException { |
91 | assertConcatenate(" foo(), '' ", "void foo()"); |
92 | assertConcatenate(" y(), 'xz' ", " 'x' ", " void y() ", " 'z' "); |
93 | assertConcatenate( |
94 | " y(), w(), 'xz' ", " 'x' ", " void y() ", " 'z' ", " void w() "); |
95 | assertConcatenate( |
96 | "(y(), 'x') + z() + (w(), '')", |
97 | " 'x' ", " void y() ", " z() ", " void w() "); |
98 | assertConcatenate( |
99 | " x(), '' + y() ", "void x()", "y()"); |
100 | assertConcatenate( |
101 | " x() + (y(), '') ", "x()", "void y()"); |
102 | } |
103 | |
104 | private void assertConcatenate(String goldenJs, String... parts) |
105 | throws ParseException { |
106 | JsConcatenator concat = new JsConcatenator(); |
107 | for (String part : parts) { |
108 | Expression e = jsExpr(fromString(part)); |
109 | if (Operation.is(e, Operator.VOID)) { |
110 | concat.forSideEffect(((Operation) e).children().get(0)); |
111 | } else { |
112 | concat.append(e); |
113 | } |
114 | } |
115 | assertEquals( |
116 | render(jsExpr(fromString(goldenJs))), |
117 | render(concat.toExpression(true))); |
118 | } |
119 | } |