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() { |
6a488035 TO |
38 | $cases = array(); |
39 | $cases[] = array( | |
40 | '', | |
41 | array(), | |
42 | ); | |
39b959db SL |
43 | // missing ts |
44 | $cases[] = array( | |
6a488035 TO |
45 | 'alert("Hello world")', |
46 | array(), | |
47 | ); | |
39b959db SL |
48 | // basic function call |
49 | $cases[] = array( | |
6a488035 TO |
50 | 'alert(ts("Hello world"));', |
51 | array('Hello world'), | |
52 | ); | |
39b959db SL |
53 | // with arg |
54 | $cases[] = array( | |
6a488035 TO |
55 | 'alert(ts("Hello world", {1: "whiz"}));', |
56 | array('Hello world'), | |
57 | ); | |
39b959db SL |
58 | // not really ts() |
59 | $cases[] = array( | |
6a488035 TO |
60 | 'alert(clients("Hello world"));', |
61 | array(), | |
62 | ); | |
39b959db SL |
63 | // not really ts() |
64 | $cases[] = array( | |
6a488035 TO |
65 | 'alert(clients("Hello world", {1: "whiz"}));', |
66 | array(), | |
67 | ); | |
39b959db SL |
68 | // with arg |
69 | $cases[] = array( | |
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", | |
6a488035 TO |
78 | array('Hello'), |
79 | ); | |
39b959db SL |
80 | // duplicate |
81 | $cases[] = array( | |
6a488035 TO |
82 | 'alert(ts("Hello world") + "-" + ts("Hello world"));', |
83 | array('Hello world'), | |
84 | ); | |
39b959db SL |
85 | // two strings, addition |
86 | $cases[] = array( | |
6a488035 TO |
87 | 'alert(ts("Hello world") + "-" + ts("How do you do?"));', |
88 | array('Hello world', 'How do you do?'), | |
89 | ); | |
39b959db SL |
90 | // two strings, separate calls |
91 | $cases[] = array( | |
6a488035 TO |
92 | 'alert(ts("Hello world");\nalert(ts("How do you do?"));', |
93 | array('Hello world', 'How do you do?'), | |
94 | ); | |
95 | $cases[] = array( | |
96 | 'alert(ts(\'Single quoted\'));', | |
97 | array('Single quoted'), | |
98 | ); | |
39b959db SL |
99 | // unclear string |
100 | $cases[] = array( | |
6a488035 TO |
101 | 'alert(ts(message));', |
102 | array(), | |
103 | ); | |
39b959db SL |
104 | // ts() within a string |
105 | $cases[] = array( | |
6a488035 TO |
106 | 'alert(ts("Does the ts(\'example\') notation work?"));', |
107 | array('Does the ts(\'example\') notation work?'), | |
108 | ); | |
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 | ||
139 | $cases = array(); | |
140 | $cases[] = array(array($a), "$a"); | |
141 | $cases[] = array(array($b), "$b"); | |
142 | $cases[] = array(array($c), "$c"); | |
143 | $cases[] = array(array($d), "$d"); | |
144 | $cases[] = array(array($m), "$m"); | |
145 | $cases[] = array(array($a, $b), "$ab"); | |
146 | $cases[] = array(array($a, $m, $b), "$a$m$b"); | |
147 | $cases[] = array(array($a, $d), "$a$d"); | |
148 | $cases[] = array(array($a, $d, $b), "$a$d$b"); | |
149 | $cases[] = array(array($a, $b, $c), "$abc"); | |
150 | $cases[] = array(array($a, $b, $d, $c, $b), "$ab$d$cb"); | |
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, | |
162 | array('angular', '$', '_'), | |
163 | array('angular', 'CRM.$', 'CRM._') | |
164 | ); | |
165 | $this->assertEquals($expectedOutput, implode("", $actualOutput)); | |
166 | } | |
b047e061 TO |
167 | |
168 | public function stripCommentsExamples() { | |
169 | $cases = array(); | |
170 | $cases[] = array( | |
171 | "a();\n//# sourceMappingURL=../foo/bar/baz.js\nb();", | |
172 | "a();\n\nb();", | |
173 | ); | |
174 | $cases[] = array( | |
175 | "// foo\na();", | |
176 | "\na();", | |
177 | ); | |
178 | $cases[] = array( | |
179 | "b();\n // foo", | |
180 | "b();\n", | |
181 | ); | |
182 | $cases[] = array( | |
183 | "/// foo\na();\n\t \t//bar\nb();\n// whiz", | |
184 | "\na();\n\nb();\n", | |
185 | ); | |
186 | $cases[] = array( | |
187 | "alert('//# sourceMappingURL=../foo/bar/baz.js');\n//zoop\na();", | |
188 | "alert('//# sourceMappingURL=../foo/bar/baz.js');\n\na();", | |
189 | ); | |
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], | |
206 | ['false', FALSE], | |
207 | ['null', NULL], | |
9511ca30 | 208 | ['"true"', 'true'], |
a49c5ad6 | 209 | ['0.5', 0.5], |
9511ca30 | 210 | [" {}", []], |
a49c5ad6 | 211 | ["[]", []], |
9511ca30 CW |
212 | ["{ }", []], |
213 | [" [ ]", []], | |
214 | [" [ 2 ]", [2]], | |
215 | [ | |
216 | '{a: ["foo", \'bar\'], "b": {a: [\'foo\', "bar"], b: {\'a\': ["foo", "bar"], b: {}}}}', | |
217 | ['a' => ['foo', 'bar'], 'b' => ['a' => ['foo', 'bar'], 'b' => ['a' => ['foo', 'bar'], 'b' => []]]], | |
218 | ], | |
219 | [ | |
220 | ' [{a: {aa: true}, b: [false, null, {x: 1, y: 2, z: 3}] , "c": -1}, ["fee", "fie", \'foe\']]', | |
221 | [['a' => ['aa' => TRUE], 'b' => [FALSE, NULL, ['x' => 1, 'y' => 2, 'z' => 3]], "c" => -1], ["fee", "fie", "foe"]], | |
222 | ], | |
a49c5ad6 CW |
223 | ]; |
224 | } | |
225 | ||
226 | /** | |
227 | * @param string $input | |
228 | * @param string $expectedOutput | |
229 | * @dataProvider decodeExamples | |
230 | */ | |
231 | public function testDecode($input, $expectedOutput) { | |
232 | $this->assertEquals($expectedOutput, CRM_Utils_JS::decode($input)); | |
233 | } | |
234 | ||
9511ca30 CW |
235 | /** |
236 | * @return array | |
237 | */ | |
3203414a CW |
238 | public static function objectExamples() { |
239 | return [ | |
240 | [ | |
241 | '{a: \'Apple\', \'b\': "Banana", "c ": [1,2,3]}', | |
242 | ['a' => "'Apple'", 'b' => '"Banana"', 'c ' => '[1,2,3]'], | |
9511ca30 | 243 | '{a: \'Apple\', b: "Banana", \'c \': [1,2,3]}', |
3203414a CW |
244 | ], |
245 | [ | |
9511ca30 CW |
246 | " {}", |
247 | [], | |
3203414a | 248 | "{}", |
9511ca30 CW |
249 | ], |
250 | [ | |
251 | " [ ] ", | |
3203414a | 252 | [], |
9511ca30 CW |
253 | "{}", |
254 | ], | |
255 | [ | |
256 | " {'fn' : function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\') , number : 55.5/2 } ", | |
257 | ['fn' => 'function (foo, bar, baz) { return "One, two, three"; }', 'esc' => "/[1-9]\\\\/.test('5\\\\')", 'number' => '55.5/2'], | |
258 | "{fn: function (foo, bar, baz) { return \"One, two, three\"; }, esc: /[1-9]\\\\/.test('5\\\\'), number: 55.5/2}", | |
259 | ], | |
260 | [ | |
261 | "{ string : | |
262 | 'this, has(some : weird, \\'stuff [{}!' , | |
263 | expr: sum(1, 2, 3) / 2 + 1, ' notes ' : [Do, re mi], | |
264 | }", | |
265 | ['string' => "'this, has(some : weird, \\'stuff [{}!'", 'expr' => 'sum(1, 2, 3) / 2 + 1', ' notes ' => "[Do, re mi]"], | |
266 | "{string: 'this, has(some : weird, \\'stuff [{}!', expr: sum(1, 2, 3) / 2 + 1, ' notes ': [Do, re mi]}", | |
3203414a CW |
267 | ], |
268 | [ | |
9511ca30 CW |
269 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , \'foo\&\': getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', |
270 | ['status' => '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', 'foo&' => 'getFoo("Some \"quoted\" thing")', "ba'[(r" => 'function() {return "bar"}'], | |
271 | '{status: /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', "foo&": getFoo("Some \"quoted\" thing"), "ba\'[(r": function() {return "bar"}}', | |
3203414a CW |
272 | ], |
273 | [ | |
9511ca30 CW |
274 | '{"some\"key": typeof foo === \'number\' ? true : false , "O\'Really?": ",((,", \'A"quote"\': 1 + 1 , "\\\\\\&\\/" : 0}', |
275 | ['some"key' => 'typeof foo === \'number\' ? true : false', "O'Really?" => '",((,"', 'A"quote"' => '1 + 1', '\\&/' => '0'], | |
276 | '{\'some"key\': typeof foo === \'number\' ? true : false, "O\'Really?": ",((,", \'A"quote"\': 1 + 1, "\\\\&/": 0}', | |
3203414a CW |
277 | ], |
278 | [ | |
9511ca30 CW |
279 | '[foo ? 1 : 2 , 3 , function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\' , 3.14 ]', |
280 | ['foo ? 1 : 2', '3', 'function() {return 1 + 1;}', '/^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\'', '3.14'], | |
281 | '[foo ? 1 : 2, 3, function() {return 1 + 1;}, /^http:\/\/civicrm\.com/.test(url) ? \'good\' : \'bad\', 3.14]', | |
3203414a CW |
282 | ], |
283 | ]; | |
284 | } | |
285 | ||
286 | /** | |
9511ca30 CW |
287 | * Test converting a js string to a php array and back again. |
288 | * | |
3203414a | 289 | * @param string $input |
9511ca30 CW |
290 | * @param string $expectedPHP |
291 | * @param $expectedJS | |
3203414a CW |
292 | * @dataProvider objectExamples |
293 | */ | |
9511ca30 CW |
294 | public function testObjectToAndFromString($input, $expectedPHP, $expectedJS) { |
295 | $objectProps = CRM_Utils_JS::getRawProps($input); | |
296 | $this->assertEquals($expectedPHP, $objectProps); | |
297 | $reformattedJS = CRM_Utils_JS::writeObject($objectProps); | |
298 | $this->assertEquals($expectedJS, $reformattedJS); | |
299 | $this->assertEquals($expectedPHP, CRM_Utils_JS::getRawProps($reformattedJS)); | |
3203414a CW |
300 | } |
301 | ||
6a488035 | 302 | } |