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 | ||
9511ca30 CW |
240 | /** |
241 | * @return array | |
242 | */ | |
3203414a CW |
243 | public static function objectExamples() { |
244 | return [ | |
245 | [ | |
246 | '{a: \'Apple\', \'b\': "Banana", "c ": [1,2,3]}', | |
247 | ['a' => "'Apple'", 'b' => '"Banana"', 'c ' => '[1,2,3]'], | |
9511ca30 | 248 | '{a: \'Apple\', b: "Banana", \'c \': [1,2,3]}', |
3203414a CW |
249 | ], |
250 | [ | |
9511ca30 CW |
251 | " {}", |
252 | [], | |
3203414a | 253 | "{}", |
9511ca30 CW |
254 | ], |
255 | [ | |
256 | " [ ] ", | |
3203414a | 257 | [], |
9511ca30 CW |
258 | "{}", |
259 | ], | |
260 | [ | |
261 | " {'fn' : function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\') , number : 55.5/2 } ", | |
262 | ['fn' => 'function (foo, bar, baz) { return "One, two, three"; }', 'esc' => "/[1-9]\\\\/.test('5\\\\')", 'number' => '55.5/2'], | |
263 | "{fn: function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\'), number: 55.5/2}", | |
264 | ], | |
265 | [ | |
266 | "{ string : | |
267 | 'this, has(some : weird, \\'stuff [{}!' , | |
268 | expr: sum(1, 2, 3) / 2 + 1, ' notes ' : [Do, re mi], | |
269 | }", | |
270 | ['string' => "'this, has(some : weird, \\'stuff [{}!'", 'expr' => 'sum(1, 2, 3) / 2 + 1', ' notes ' => "[Do, re mi]"], | |
271 | "{string: 'this, has(some : weird, \\'stuff [{}!', expr: sum(1, 2, 3) / 2 + 1, ' notes ': [Do, re mi]}", | |
3203414a CW |
272 | ], |
273 | [ | |
9511ca30 CW |
274 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , \'foo\&\': getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', |
275 | ['status' => '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', 'foo&' => 'getFoo("Some \"quoted\" thing")', "ba'[(r" => 'function() {return "bar"}'], | |
276 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', "foo&": getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', | |
3203414a CW |
277 | ], |
278 | [ | |
9511ca30 CW |
279 | '{"some\"key": typeof foo === \'number\' ? true : false , "O\'Really?": ",((,", \'A"quote"\': 1 + 1 , "\\\\\\&\\/" : 0}', |
280 | ['some"key' => 'typeof foo === \'number\' ? true : false', "O'Really?" => '",((,"', 'A"quote"' => '1 + 1', '\\&/' => '0'], | |
281 | '{\'some"key\': typeof foo === \'number\' ? true : false, "O\'Really?": ",((,", \'A"quote"\': 1 + 1, "\\\\&/": 0}', | |
3203414a CW |
282 | ], |
283 | [ | |
9511ca30 CW |
284 | '[foo ? 1 : 2 , 3 , function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , 3.14 ]', |
285 | ['foo ? 1 : 2', '3', 'function() {return 1 + 1;}', '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', '3.14'], | |
286 | '[foo ? 1 : 2, 3, function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', 3.14]', | |
3203414a CW |
287 | ], |
288 | ]; | |
289 | } | |
290 | ||
291 | /** | |
9511ca30 CW |
292 | * Test converting a js string to a php array and back again. |
293 | * | |
3203414a | 294 | * @param string $input |
9511ca30 CW |
295 | * @param string $expectedPHP |
296 | * @param $expectedJS | |
3203414a CW |
297 | * @dataProvider objectExamples |
298 | */ | |
9511ca30 CW |
299 | public function testObjectToAndFromString($input, $expectedPHP, $expectedJS) { |
300 | $objectProps = CRM_Utils_JS::getRawProps($input); | |
301 | $this->assertEquals($expectedPHP, $objectProps); | |
302 | $reformattedJS = CRM_Utils_JS::writeObject($objectProps); | |
303 | $this->assertEquals($expectedJS, $reformattedJS); | |
304 | $this->assertEquals($expectedPHP, CRM_Utils_JS::getRawProps($reformattedJS)); | |
3203414a CW |
305 | } |
306 | ||
6a488035 | 307 | } |