Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
2fe49090 | 4 | | CiviCRM version 5 | |
6a488035 | 5 | +--------------------------------------------------------------------+ |
6b83d5bd | 6 | | Copyright CiviCRM LLC (c) 2004-2019 | |
6a488035 TO |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License and the CiviCRM Licensing Exception along | | |
21 | | with this program; if not, contact CiviCRM LLC | | |
22 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
23 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
25 | +--------------------------------------------------------------------+ | |
e70a7fc0 | 26 | */ |
6a488035 | 27 | |
6a488035 TO |
28 | /** |
29 | * Tests for linking to resource files | |
acb109b7 | 30 | * @group headless |
6a488035 TO |
31 | */ |
32 | class CRM_Utils_JSTest extends CiviUnitTestCase { | |
39b959db | 33 | |
4cbe18b8 EM |
34 | /** |
35 | * @return array | |
36 | */ | |
00be9182 | 37 | public function translateExamples() { |
9099cab3 CW |
38 | $cases = []; |
39 | $cases[] = [ | |
6a488035 | 40 | '', |
9099cab3 CW |
41 | [], |
42 | ]; | |
39b959db | 43 | // missing ts |
9099cab3 | 44 | $cases[] = [ |
6a488035 | 45 | 'alert("Hello world")', |
9099cab3 CW |
46 | [], |
47 | ]; | |
39b959db | 48 | // basic function call |
9099cab3 | 49 | $cases[] = [ |
6a488035 | 50 | 'alert(ts("Hello world"));', |
9099cab3 CW |
51 | ['Hello world'], |
52 | ]; | |
39b959db | 53 | // with arg |
9099cab3 | 54 | $cases[] = [ |
6a488035 | 55 | 'alert(ts("Hello world", {1: "whiz"}));', |
9099cab3 CW |
56 | ['Hello world'], |
57 | ]; | |
39b959db | 58 | // not really ts() |
9099cab3 | 59 | $cases[] = [ |
6a488035 | 60 | 'alert(clients("Hello world"));', |
9099cab3 CW |
61 | [], |
62 | ]; | |
39b959db | 63 | // not really ts() |
9099cab3 | 64 | $cases[] = [ |
6a488035 | 65 | 'alert(clients("Hello world", {1: "whiz"}));', |
9099cab3 CW |
66 | [], |
67 | ]; | |
39b959db | 68 | // with arg |
9099cab3 | 69 | $cases[] = [ |
0c7a8599 TO |
70 | "\n" . |
71 | "public function whits() {\n" . | |
72 | " for (a in b) {\n" . | |
73 | " mitts(\"wallaby\", function(zoo) {\n" . | |
74 | " alert(zoo + ts(\"Hello\"))\n" . | |
75 | " });\n" . | |
76 | " }\n" . | |
77 | "}\n", | |
9099cab3 CW |
78 | ['Hello'], |
79 | ]; | |
39b959db | 80 | // duplicate |
9099cab3 | 81 | $cases[] = [ |
6a488035 | 82 | 'alert(ts("Hello world") + "-" + ts("Hello world"));', |
9099cab3 CW |
83 | ['Hello world'], |
84 | ]; | |
39b959db | 85 | // two strings, addition |
9099cab3 | 86 | $cases[] = [ |
6a488035 | 87 | 'alert(ts("Hello world") + "-" + ts("How do you do?"));', |
9099cab3 CW |
88 | ['Hello world', 'How do you do?'], |
89 | ]; | |
39b959db | 90 | // two strings, separate calls |
9099cab3 | 91 | $cases[] = [ |
6a488035 | 92 | 'alert(ts("Hello world");\nalert(ts("How do you do?"));', |
9099cab3 CW |
93 | ['Hello world', 'How do you do?'], |
94 | ]; | |
95 | $cases[] = [ | |
6a488035 | 96 | 'alert(ts(\'Single quoted\'));', |
9099cab3 CW |
97 | ['Single quoted'], |
98 | ]; | |
39b959db | 99 | // unclear string |
9099cab3 | 100 | $cases[] = [ |
6a488035 | 101 | 'alert(ts(message));', |
9099cab3 CW |
102 | [], |
103 | ]; | |
39b959db | 104 | // ts() within a string |
9099cab3 | 105 | $cases[] = [ |
6a488035 | 106 | 'alert(ts("Does the ts(\'example\') notation work?"));', |
9099cab3 CW |
107 | ['Does the ts(\'example\') notation work?'], |
108 | ]; | |
6a488035 TO |
109 | return $cases; |
110 | } | |
111 | ||
112 | /** | |
113 | * @param string $jsCode | |
114 | * @param array $expectedStrings | |
115 | * @dataProvider translateExamples | |
116 | */ | |
00be9182 | 117 | public function testParseStrings($jsCode, $expectedStrings) { |
6a488035 TO |
118 | $actualStrings = CRM_Utils_JS::parseStrings($jsCode); |
119 | sort($expectedStrings); | |
120 | sort($actualStrings); | |
121 | $this->assertEquals($expectedStrings, $actualStrings); | |
122 | } | |
96025800 | 123 | |
ad295ca9 TO |
124 | public function dedupeClosureExamples() { |
125 | // Each example string here is named for its body, eg the body of $a calls "a()". | |
126 | $a = "(function (angular, $, _) {\na();\n})(angular, CRM.$, CRM._);"; | |
127 | $b = "(function(angular,$,_){\nb();\n})(angular,CRM.$,CRM._);"; | |
128 | $c = "(function( angular, $,_) {\nc();\n})(angular,CRM.$, CRM._);"; | |
129 | $d = "(function (angular, $, _, whiz) {\nd();\n})(angular, CRM.$, CRM._, CRM.whizbang);"; | |
130 | $m = "alert('i is the trickster (function( angular, $,_) {\nm();\n})(angular,CRM.$, CRM._);)'"; | |
131 | // Note: $d has a fundamentally different closure. | |
132 | ||
133 | // Each example string here is a deduped combination of others, | |
134 | // eg "$ab" is the deduping of $a+$b. | |
135 | $ab = "(function (angular, $, _) {\na();\n\nb();\n})(angular,CRM.$,CRM._);"; | |
136 | $abc = "(function (angular, $, _) {\na();\n\nb();\n\nc();\n})(angular,CRM.$, CRM._);"; | |
137 | $cb = "(function( angular, $,_) {\nc();\n\nb();\n})(angular,CRM.$,CRM._);"; | |
138 | ||
9099cab3 CW |
139 | $cases = []; |
140 | $cases[] = [[$a], "$a"]; | |
141 | $cases[] = [[$b], "$b"]; | |
142 | $cases[] = [[$c], "$c"]; | |
143 | $cases[] = [[$d], "$d"]; | |
144 | $cases[] = [[$m], "$m"]; | |
145 | $cases[] = [[$a, $b], "$ab"]; | |
146 | $cases[] = [[$a, $m, $b], "$a$m$b"]; | |
147 | $cases[] = [[$a, $d], "$a$d"]; | |
148 | $cases[] = [[$a, $d, $b], "$a$d$b"]; | |
149 | $cases[] = [[$a, $b, $c], "$abc"]; | |
150 | $cases[] = [[$a, $b, $d, $c, $b], "$ab$d$cb"]; | |
ad295ca9 TO |
151 | return $cases; |
152 | } | |
153 | ||
154 | /** | |
155 | * @param array $scripts | |
156 | * @param string $expectedOutput | |
157 | * @dataProvider dedupeClosureExamples | |
158 | */ | |
159 | public function testDedupeClosure($scripts, $expectedOutput) { | |
160 | $actualOutput = CRM_Utils_JS::dedupeClosures( | |
161 | $scripts, | |
9099cab3 CW |
162 | ['angular', '$', '_'], |
163 | ['angular', 'CRM.$', 'CRM._'] | |
ad295ca9 TO |
164 | ); |
165 | $this->assertEquals($expectedOutput, implode("", $actualOutput)); | |
166 | } | |
b047e061 TO |
167 | |
168 | public function stripCommentsExamples() { | |
9099cab3 CW |
169 | $cases = []; |
170 | $cases[] = [ | |
b047e061 TO |
171 | "a();\n//# sourceMappingURL=../foo/bar/baz.js\nb();", |
172 | "a();\n\nb();", | |
9099cab3 CW |
173 | ]; |
174 | $cases[] = [ | |
b047e061 TO |
175 | "// foo\na();", |
176 | "\na();", | |
9099cab3 CW |
177 | ]; |
178 | $cases[] = [ | |
b047e061 TO |
179 | "b();\n // foo", |
180 | "b();\n", | |
9099cab3 CW |
181 | ]; |
182 | $cases[] = [ | |
b047e061 TO |
183 | "/// foo\na();\n\t \t//bar\nb();\n// whiz", |
184 | "\na();\n\nb();\n", | |
9099cab3 CW |
185 | ]; |
186 | $cases[] = [ | |
b047e061 TO |
187 | "alert('//# sourceMappingURL=../foo/bar/baz.js');\n//zoop\na();", |
188 | "alert('//# sourceMappingURL=../foo/bar/baz.js');\n\na();", | |
9099cab3 | 189 | ]; |
b047e061 TO |
190 | return $cases; |
191 | } | |
192 | ||
193 | /** | |
194 | * @param string $input | |
195 | * @param string $expectedOutput | |
196 | * @dataProvider stripCommentsExamples | |
197 | */ | |
198 | public function testStripComments($input, $expectedOutput) { | |
199 | $this->assertEquals($expectedOutput, CRM_Utils_JS::stripComments($input)); | |
200 | } | |
201 | ||
a49c5ad6 CW |
202 | public static function decodeExamples() { |
203 | return [ | |
204 | ['{a: \'Apple\', \'b\': "Banana", c: [1, 2, 3]}', ['a' => 'Apple', 'b' => 'Banana', 'c' => [1, 2, 3]]], | |
205 | ['true', TRUE], | |
d9c7a051 | 206 | [' ', NULL], |
a49c5ad6 CW |
207 | ['false', FALSE], |
208 | ['null', NULL], | |
9511ca30 | 209 | ['"true"', 'true'], |
a49c5ad6 | 210 | ['0.5', 0.5], |
9511ca30 | 211 | [" {}", []], |
a49c5ad6 | 212 | ["[]", []], |
9511ca30 CW |
213 | ["{ }", []], |
214 | [" [ ]", []], | |
215 | [" [ 2 ]", [2]], | |
d9c7a051 CW |
216 | [ |
217 | '{a: "parse error no closing bracket"', | |
218 | NULL, | |
219 | ], | |
9511ca30 CW |
220 | [ |
221 | '{a: ["foo", \'bar\'], "b": {a: [\'foo\', "bar"], b: {\'a\': ["foo", "bar"], b: {}}}}', | |
222 | ['a' => ['foo', 'bar'], 'b' => ['a' => ['foo', 'bar'], 'b' => ['a' => ['foo', 'bar'], 'b' => []]]], | |
223 | ], | |
224 | [ | |
225 | ' [{a: {aa: true}, b: [false, null, {x: 1, y: 2, z: 3}] , "c": -1}, ["fee", "fie", \'foe\']]', | |
226 | [['a' => ['aa' => TRUE], 'b' => [FALSE, NULL, ['x' => 1, 'y' => 2, 'z' => 3]], "c" => -1], ["fee", "fie", "foe"]], | |
227 | ], | |
a49c5ad6 CW |
228 | ]; |
229 | } | |
230 | ||
231 | /** | |
232 | * @param string $input | |
233 | * @param string $expectedOutput | |
234 | * @dataProvider decodeExamples | |
235 | */ | |
236 | public function testDecode($input, $expectedOutput) { | |
237 | $this->assertEquals($expectedOutput, CRM_Utils_JS::decode($input)); | |
238 | } | |
239 | ||
89b24877 CW |
240 | public static function encodeExamples() { |
241 | return [ | |
242 | [ | |
10515677 CW |
243 | ['a' => 'Apple', 'b' => 'Banana', 'c' => [0, -2, 3.15]], |
244 | "{a: 'Apple', b: 'Banana', c: [0, -2, 3.15]}", | |
89b24877 CW |
245 | ], |
246 | [ | |
10515677 CW |
247 | ['a' => ['foo', 'bar'], 'b' => ["'a'" => ['foo/bar&', 'bar(foo)'], 'b' => ['a' => ["fo'oo", '"bar"'], 'b' => []]]], |
248 | "{a: ['foo', 'bar'], b: {\"'a'\": ['foo/bar&', 'bar(foo)'], b: {a: ['fo\\'oo', '\"bar\"'], b: {}}}}", | |
89b24877 CW |
249 | ], |
250 | [TRUE, 'true'], | |
251 | [' ', "' '"], | |
252 | [FALSE, 'false'], | |
253 | [NULL, 'null'], | |
254 | ['true', "'true'"], | |
10515677 | 255 | ['"false"', "'\"false\"'"], |
89b24877 CW |
256 | ['0.5', "'0.5'"], |
257 | [0.5, '0.5'], | |
258 | [[], "{}"], | |
259 | ]; | |
260 | } | |
261 | ||
262 | /** | |
263 | * @param string $input | |
264 | * @param string $expectedOutput | |
265 | * @dataProvider encodeExamples | |
266 | */ | |
267 | public function testEncode($input, $expectedOutput) { | |
10515677 CW |
268 | $result = CRM_Utils_JS::encode($input); |
269 | $this->assertEquals($expectedOutput, $result); | |
270 | $this->assertEquals($input, CRM_Utils_JS::decode($result)); | |
89b24877 CW |
271 | } |
272 | ||
9511ca30 CW |
273 | /** |
274 | * @return array | |
275 | */ | |
3203414a CW |
276 | public static function objectExamples() { |
277 | return [ | |
278 | [ | |
279 | '{a: \'Apple\', \'b\': "Banana", "c ": [1,2,3]}', | |
280 | ['a' => "'Apple'", 'b' => '"Banana"', 'c ' => '[1,2,3]'], | |
9511ca30 | 281 | '{a: \'Apple\', b: "Banana", \'c \': [1,2,3]}', |
3203414a CW |
282 | ], |
283 | [ | |
9511ca30 CW |
284 | " {}", |
285 | [], | |
3203414a | 286 | "{}", |
9511ca30 CW |
287 | ], |
288 | [ | |
289 | " [ ] ", | |
3203414a | 290 | [], |
9511ca30 CW |
291 | "{}", |
292 | ], | |
293 | [ | |
294 | " {'fn' : function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\') , number : 55.5/2 } ", | |
295 | ['fn' => 'function (foo, bar, baz) { return "One, two, three"; }', 'esc' => "/[1-9]\\\\/.test('5\\\\')", 'number' => '55.5/2'], | |
296 | "{fn: function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\'), number: 55.5/2}", | |
297 | ], | |
298 | [ | |
299 | "{ string : | |
300 | 'this, has(some : weird, \\'stuff [{}!' , | |
301 | expr: sum(1, 2, 3) / 2 + 1, ' notes ' : [Do, re mi], | |
302 | }", | |
303 | ['string' => "'this, has(some : weird, \\'stuff [{}!'", 'expr' => 'sum(1, 2, 3) / 2 + 1', ' notes ' => "[Do, re mi]"], | |
304 | "{string: 'this, has(some : weird, \\'stuff [{}!', expr: sum(1, 2, 3) / 2 + 1, ' notes ': [Do, re mi]}", | |
3203414a CW |
305 | ], |
306 | [ | |
9511ca30 CW |
307 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , \'foo\&\': getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', |
308 | ['status' => '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', 'foo&' => 'getFoo("Some \"quoted\" thing")', "ba'[(r" => 'function() {return "bar"}'], | |
309 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', "foo&": getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', | |
3203414a CW |
310 | ], |
311 | [ | |
9511ca30 CW |
312 | '{"some\"key": typeof foo === \'number\' ? true : false , "O\'Really?": ",((,", \'A"quote"\': 1 + 1 , "\\\\\\&\\/" : 0}', |
313 | ['some"key' => 'typeof foo === \'number\' ? true : false', "O'Really?" => '",((,"', 'A"quote"' => '1 + 1', '\\&/' => '0'], | |
314 | '{\'some"key\': typeof foo === \'number\' ? true : false, "O\'Really?": ",((,", \'A"quote"\': 1 + 1, "\\\\&/": 0}', | |
3203414a CW |
315 | ], |
316 | [ | |
9511ca30 CW |
317 | '[foo ? 1 : 2 , 3 , function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , 3.14 ]', |
318 | ['foo ? 1 : 2', '3', 'function() {return 1 + 1;}', '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', '3.14'], | |
319 | '[foo ? 1 : 2, 3, function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', 3.14]', | |
3203414a CW |
320 | ], |
321 | ]; | |
322 | } | |
323 | ||
324 | /** | |
9511ca30 CW |
325 | * Test converting a js string to a php array and back again. |
326 | * | |
3203414a | 327 | * @param string $input |
9511ca30 CW |
328 | * @param string $expectedPHP |
329 | * @param $expectedJS | |
3203414a CW |
330 | * @dataProvider objectExamples |
331 | */ | |
9511ca30 CW |
332 | public function testObjectToAndFromString($input, $expectedPHP, $expectedJS) { |
333 | $objectProps = CRM_Utils_JS::getRawProps($input); | |
334 | $this->assertEquals($expectedPHP, $objectProps); | |
335 | $reformattedJS = CRM_Utils_JS::writeObject($objectProps); | |
336 | $this->assertEquals($expectedJS, $reformattedJS); | |
337 | $this->assertEquals($expectedPHP, CRM_Utils_JS::getRawProps($reformattedJS)); | |
3203414a CW |
338 | } |
339 | ||
6a488035 | 340 | } |