Commit | Line | Data |
---|---|---|
171bbab3 | 1 | (in-package :ps-test) |
13b8268e VS |
2 | |
3 | ;;; Hand-written unit tests | |
e2932347 LC |
4 | |
5 | (eval-when (:compile-toplevel :load-toplevel :execute) | |
6 | (def-suite ps-tests)) | |
7 | (in-suite ps-tests) | |
8 | ||
9 | (test-ps-js plus-is-not-commutative | |
10 | (setf x (+ "before" x "after")) | |
72332f2a | 11 | "x = 'before' + x + 'after';") |
e2932347 LC |
12 | |
13 | (test-ps-js plus-works-if-first | |
14 | (setf x (+ x "middle" "after")) | |
72332f2a | 15 | "x += 'middle' + 'after';") |
e2932347 LC |
16 | |
17 | (test-ps-js setf-side-effects | |
18 | (progn | |
19 | (let ((x 10)) | |
20 | (defun side-effect() | |
21 | (setf x 4) | |
22 | (return 3)) | |
23 | (setf x (+ 2 (side-effect) x 5)))) | |
24 | " | |
25 | var x = 10; | |
26 | function sideEffect() { | |
27 | x = 4; | |
28 | return 3; | |
29 | }; | |
30 | x = 2 + sideEffect() + x + 5;") | |
31 | ;; Parenscript used to optimize to much: | |
32 | ;; var x = 10; | |
33 | ;; function sideEffect() { | |
34 | ;; x = 4; | |
35 | ;; return 3; | |
36 | ;; }; | |
37 | ;; x += 2 + sideEffect() + 5; | |
38 | ;; | |
39 | ;; Which is 20, not 14 | |
40 | ||
41 | ||
42 | (test-ps-js dot-notation-bug | |
43 | (.match (+ "" x) "foo") | |
44 | "('' + x).match('foo')") | |
45 | ||
46 | (test-ps-js method-call-op-form (.to-string (+ "" x)) "('' + x).toString()") | |
cf460f93 | 47 | (test-ps-js method-call-number (.to-string 10) "( 10 ).toString()") |
e2932347 LC |
48 | (test-ps-js method-call-string (.to-string "hi") "'hi'.toString()") |
49 | (test-ps-js method-call-lit-object | |
50 | (.to-string (create :to-string (lambda () | |
51 | (return "it works")))) | |
cf460f93 | 52 | "( { toString : function () { return 'it works'; } } ).toString()") |
e2932347 LC |
53 | |
54 | (test-ps-js method-call-variable | |
55 | (.to-string x) | |
56 | "x.toString()") | |
57 | ||
58 | (test-ps-js method-call-array | |
59 | (.to-string (list 10 20)) | |
cf460f93 | 60 | "[ 10, 20 ].toString()") |
e2932347 LC |
61 | (test-ps-js method-call-fn-call |
62 | (.to-string (foo)) | |
63 | "foo().toString()") | |
64 | (test-ps-js method-call-lambda-fn | |
65 | (.to-string (lambda () (alert 10))) | |
cf460f93 | 66 | "( function () { alert(10); } ).toString()") |
e2932347 LC |
67 | (test-ps-js method-call-lambda-call |
68 | (.to-string ((lambda (x) (return x)) 10)) | |
b44afd8f | 69 | "(function (x) { return x; })(10).toString()") |
e2932347 LC |
70 | |
71 | (test no-whitespace-before-dot | |
905f534e | 72 | (let* ((str (compile-script '(.to-string ((lambda (x) (return x)) 10)))) |
e2932347 LC |
73 | (dot-pos (position #\. str :test #'char=)) |
74 | (char-before (elt str (1- dot-pos))) | |
75 | (a-parenthesis #\))) | |
76 | (is (char= char-before a-parenthesis)))) | |
77 | ||
78 | ;; A problem with long nested operator, when the statement spanned several rows | |
79 | ;; the rows would not be joined together correctly. | |
80 | (test-ps-js bug-dwim-join | |
8bb28ead VS |
81 | (alert (ps-html ((:div :id 777 |
82 | :style (css-inline :border "1pxsssssssssss" | |
83 | :font-size "x-small" | |
84 | :height (* 2 200) | |
85 | :width (* 2 300)))))) | |
b44afd8f | 86 | "alert('<div id=\"777\" style=\"' |
e2932347 LC |
87 | + ('border:1pxsssssssssss;font-size:x-small;height:' + 2 * 200 + ';width:' |
88 | + 2 * 300) | |
89 | + '\"></div>')") ;";This line should start with a plus character. | |
90 | ||
91 | ||
92 | (test-ps-js simple-slot-value | |
93 | (let ((foo (create :a 1))) | |
94 | (alert (slot-value foo 'a))) | |
07afea38 VS |
95 | "var foo = { a : 1 }; |
96 | alert(foo.a);") | |
e2932347 LC |
97 | |
98 | (test-ps-js buggy-slot-value | |
99 | (let ((foo (create :a 1)) | |
100 | (slot-name "a")) | |
101 | (alert (slot-value foo slot-name))) | |
07afea38 | 102 | " var foo = { a : 1 }; |
e2932347 LC |
103 | var slotName = 'a'; |
104 | alert(foo[slotName]); | |
07afea38 | 105 | "); Last line was alert(foo.slotName) before bug-fix. |
e2932347 LC |
106 | |
107 | (test-ps-js buggy-slot-value-two | |
108 | (slot-value foo (get-slot-name)) | |
109 | "foo[getSlotName()]") | |
110 | ||
111 | (test-ps-js old-case-is-now-switch | |
112 | ;; Switch was "case" before, but that was very non-lispish. | |
113 | ;; For example, this code makes three messages and not one | |
114 | ;; which may have been expected. This is because a switch | |
115 | ;; statment must have a break statement for it to return | |
116 | ;; after the alert. Otherwise it continues on the next | |
117 | ;; clause. | |
118 | (switch (aref blorg i) | |
119 | (1 (alert "one")) | |
120 | (2 (alert "two")) | |
121 | (default (alert "default clause"))) | |
122 | "switch (blorg[i]) { | |
123 | case 1: alert('one'); | |
124 | case 2: alert('two'); | |
125 | default: alert('default clause'); | |
126 | }") | |
127 | ||
128 | (test-ps-js lisp-like-case | |
129 | (case (aref blorg i) | |
130 | (1 (alert "one")) | |
131 | (2 (alert "two")) | |
132 | (default (alert "default clause"))) | |
133 | "switch (blorg[i]) { | |
134 | case 1: | |
135 | alert('one'); | |
136 | break; | |
137 | case 2: | |
138 | alert('two'); | |
139 | break; | |
140 | default: alert('default clause'); | |
141 | }") | |
142 | ||
143 | ||
144 | (test-ps-js even-lispier-case | |
145 | (case (aref blorg i) | |
146 | ((1 2) (alert "Below three")) | |
147 | (3 (alert "Three")) | |
148 | (t (alert "Something else"))) | |
149 | "switch (blorg[i]) { | |
b44afd8f | 150 | case 1: |
e2932347 LC |
151 | case 2: |
152 | alert('Below three'); | |
153 | break; | |
154 | case 3: | |
155 | alert('Three'); | |
156 | break; | |
157 | default: alert('Something else'); | |
158 | }") | |
159 | ||
160 | (test-ps-js otherwise-case | |
161 | (case (aref blorg i) | |
162 | (1 (alert "one")) | |
e0f0d152 | 163 | (otherwise (alert "default clause"))) |
e2932347 LC |
164 | "switch (blorg[i]) { |
165 | case 1: | |
166 | alert('one'); | |
167 | break; | |
168 | default: alert('default clause'); | |
169 | }") | |
170 | ||
171 | (test escape-sequences-in-string | |
905f534e | 172 | (let ((escapes `((#\\ . #\\) |
e2932347 LC |
173 | (#\b . #\Backspace) |
174 | (#\f . ,(code-char 12)) | |
175 | ("u000B" . ,(code-char #x000b));;Vertical tab, too uncommon to bother with | |
176 | (#\n . #\Newline) | |
177 | (#\r . #\Return) | |
178 | (#\' . #\');;Double quote need not be quoted because parenscript strings are single quoted | |
179 | (#\t . #\Tab) | |
180 | ("u001F" . ,(code-char #x001f));; character below 32 | |
181 | ("u0080" . ,(code-char 128)) ;;Character over 127. Actually valid, parenscript escapes them to be sure. | |
182 | ("uABCD" . ,(code-char #xabcd)))));; Really above ascii. | |
183 | (loop for (js-escape . lisp-char) in escapes | |
905f534e | 184 | for generated = (compile-script `(let ((x ,(format nil "hello~ahi" lisp-char))))) |
07afea38 VS |
185 | for wanted = (format nil "var x = 'hello\\~ahi';" js-escape) |
186 | do (is (string= (normalize-js-code generated) wanted))))) | |
9da682ca | 187 | |
e0f0d152 VS |
188 | (test-ps-js complicated-symbol-name1 |
189 | grid-rows[foo].bar | |
190 | "gridRows[foo].bar") | |
191 | ||
192 | (test-ps-js complicated-symbol-name2 | |
193 | *grid-rows*[foo].bar | |
194 | "GRIDROWS[foo].bar") | |
cf460f93 VS |
195 | |
196 | (test-ps-js slot-value-setf | |
197 | (setf (slot-value x 'y) (+ (+ a 3) 4)) | |
72332f2a | 198 | "x.y = (a + 3) + 4;") |
eb0befe2 VS |
199 | |
200 | (test-ps-js slot-value-conditional1 | |
201 | (slot-value (if zoo foo bar) 'x) | |
202 | "(zoo ? foo : bar).x") | |
203 | ||
204 | (test-ps-js slot-value-conditional2 | |
205 | (slot-value (if (not zoo) foo bar) 'x) | |
206 | "(!zoo ? foo : bar).x") | |
207 | ||
245d0fbd | 208 | (test script-star-eval1 |
07afea38 | 209 | (is (string= "x = 1; y = 2;" (normalize-js-code (ps* '(setf x 1) '(setf y 2)))))) |
245d0fbd VS |
210 | |
211 | (test script-star-eval2 | |
07afea38 | 212 | (is (string= "x = 1;" (normalize-js-code (ps* '(setf x 1)))))) |
62b4a867 VS |
213 | |
214 | (test-ps-js slot-value-null1 | |
215 | (slot-value foo nil) | |
216 | "foo") | |
217 | ||
218 | (test-ps-js slot-value-null2 | |
219 | (slot-value foo 'nil) | |
220 | "foo") | |
221 | ||
46f794a4 RD |
222 | (test-ps-js unquoted-nil |
223 | nil | |
224 | "null") | |
225 | ||
226 | (test-ps-js list-with-single-nil | |
227 | (array 'nil) | |
228 | "[null]") | |
229 | ||
62b4a867 VS |
230 | (test-ps-js quoted-nil |
231 | 'nil | |
72332f2a VS |
232 | "null") |
233 | ||
234 | (test defsetf1 | |
235 | (ps (defsetf baz (x y) (newval) `(set-baz ,x ,y ,newval))) | |
07afea38 VS |
236 | (is (string= "var _js2 = 1; var _js3 = 2; var _js1 = 3; setBaz(_js2, _js3, _js1);" |
237 | (normalize-js-code (let ((ps:*ps-gensym-counter* 0)) | |
72332f2a | 238 | (ps (setf (baz 1 2) 3))))))) |
d989d711 | 239 | |
750651b0 VS |
240 | (test defsetf-short |
241 | (ps (defsetf baz set-baz "blah")) | |
242 | (is (string= "setBaz(1, 2, 3, 'foo');" (normalize-js-code (ps (setf (baz 1 2 3) "foo")))))) | |
243 | ||
dbb7017b VS |
244 | (test defun-setf1 |
245 | (is (and (string= (normalize-js-code (ps:ps (defun (setf some-thing) (new-val i1 i2) | |
246 | (setf (aref *some-thing* i1 i2) new-val)))) | |
13b8268e | 247 | "function __setf_someThing(newVal, i1, i2) { SOMETHING[i1][i2] = newVal; };") |
07afea38 VS |
248 | (string= (let ((ps:*ps-gensym-counter* 0)) (normalize-js-code (ps:ps (setf (some-thing 1 2) "foo")))) |
249 | "var _js2 = 1; var _js3 = 2; var _js1 = 'foo'; __setf_someThing(_js1, _js2, _js3);")))) | |
dbb7017b | 250 | |
d989d711 VS |
251 | (test-ps-js defun-optional1 |
252 | (defun test-opt (&optional x) (return (if x "yes" "no"))) | |
253 | "function testOpt(x) { | |
254 | x = undefined === x && null || x; | |
255 | return x ? 'yes' : 'no'; | |
a2434734 VS |
256 | }") |
257 | ||
258 | (test-ps-js return-nothing | |
259 | (return) | |
260 | "return null") | |
dbb7017b VS |
261 | |
262 | (test-ps-js set-timeout | |
263 | (do-set-timeout (10) (alert "foo")) | |
b44afd8f | 264 | "setTimeout(function () { alert('foo'); }, 10)") |
07afea38 VS |
265 | (test-ps-js operator-precedence |
266 | (* 3 (+ 4 5) 6) | |
267 | "3 * (4 + 5) * 6") | |
268 | ||
269 | (test-ps-js incf1 | |
270 | (incf foo bar) | |
271 | "foo += bar") | |
272 | ||
273 | (test-ps-js decf1 | |
274 | (decf foo bar) | |
275 | "foo -= bar") | |
276 | ||
49c50da4 VS |
277 | (test-ps-js incf2 |
278 | (incf x 5) | |
279 | "x += 5") | |
280 | ||
281 | (test-ps-js decf2 | |
282 | (decf y 10) | |
283 | "y -= 10") | |
284 | ||
07afea38 VS |
285 | (test-ps-js setf-conditional |
286 | (setf foo (if x 1 2)) | |
287 | "foo = x ? 1 : 2;") | |
288 | ||
289 | (test-ps-js obj-literal-numbers | |
290 | (create 1 "foo") | |
291 | "{ 1 : 'foo' }") | |
292 | ||
293 | (test-ps-js obj-literal-strings | |
294 | (create "foo" 2) | |
a805b30d VS |
295 | "{ 'foo' : 2 }") |
296 | ||
297 | (test-ps-js slot-value-string | |
298 | (slot-value foo "bar") | |
299 | "foo['bar']") | |
b44afd8f VS |
300 | |
301 | (test-ps-js slot-value-progn | |
13b8268e VS |
302 | (slot-value (progn (some-fun "abc") "123") "length") |
303 | "(someFun('abc'), '123')['length']") | |
b44afd8f VS |
304 | |
305 | (test-ps-js method-call-block | |
13b8268e VS |
306 | (.to-string (progn (some-fun "abc") "123")) |
307 | "(someFun('abc'), '123').toString()") | |
b44afd8f VS |
308 | |
309 | (test-ps-js create-blank | |
310 | (create) | |
311 | "{ }") | |
312 | ||
313 | (test-ps-js blank-object-literal | |
314 | {} | |
315 | "{ }") | |
44934751 VS |
316 | |
317 | (test-ps-js defun-rest1 | |
318 | (defun foo (&rest bar) (alert bar[1])) | |
319 | "function foo() { | |
320 | var bar = []; | |
2e593e4c VS |
321 | for (var _js2 = 0; _js2 < arguments.length - 0; _js2 = _js2 + 1) { |
322 | bar[_js2] = arguments[_js2 + 0]; | |
44934751 VS |
323 | }; |
324 | alert(bar[1]); | |
325 | }") | |
326 | ||
327 | (test-ps-js defun-rest2 | |
328 | (defun foo (baz &rest bar) (return (+ baz (aref bar 1)))) | |
2e593e4c | 329 | "function foo(baz) { |
44934751 | 330 | var bar = []; |
2e593e4c VS |
331 | for (var _js2 = 0; _js2 < arguments.length - 1; _js2 = _js2 + 1) { |
332 | bar[_js2] = arguments[_js2 + 1]; | |
44934751 VS |
333 | }; |
334 | return baz + bar[1]; | |
335 | }") | |
2e593e4c VS |
336 | |
337 | (test-ps-js defun-keyword1 | |
338 | (defun zoo (foo bar &key baz) (return (+ foo bar baz))) | |
339 | "function zoo(foo, bar, _js1) { | |
340 | _js1 = undefined === _js1 && { } || _js1; | |
341 | return foo + bar + _js1.baz; | |
342 | }") | |
b0f64e9b VS |
343 | |
344 | (test-ps-js cond1 | |
345 | (cond ((= x 1) 1)) | |
346 | "if (x == 1) { | |
347 | 1; | |
348 | }") | |
349 | ||
350 | (test-ps-js cond2 | |
13b8268e | 351 | (cond ((= x 1) 2) ((= y (* x 4)) (foo "blah") (* x y))) |
b0f64e9b VS |
352 | "if (x == 1) { |
353 | 2; | |
354 | } else if (y == x * 4) { | |
13b8268e | 355 | foo('blah'); |
b0f64e9b VS |
356 | x * y; |
357 | }") | |
5705b542 VS |
358 | |
359 | (test-ps-js if-exp-without-else-returns-null | |
360 | (return (if x 1)) | |
361 | "return x ? 1 : null") | |
362 | ||
363 | (test-ps-js progn-expression-single-statement | |
364 | (return (progn (* x y))) | |
365 | "return x * y") | |
0949f072 VS |
366 | |
367 | (test-ps-js cond-expression1 | |
13b8268e | 368 | (defun foo () (return (cond ((< 1 2) (bar "foo") (* 4 5))))) |
0949f072 | 369 | "function foo() { |
13b8268e | 370 | return 1 < 2 ? (bar('foo'), 4 * 5) : null; |
0949f072 VS |
371 | }") |
372 | ||
373 | (test-ps-js cond-expression2 | |
374 | (defun foo () (return (cond ((< 2 1) "foo") ((= 7 7) "bar")))) | |
375 | "function foo() { | |
376 | return 2 < 1 ? 'foo' : (7 == 7 ? 'bar' : null); | |
377 | }") | |
378 | ||
379 | (test-ps-js cond-expression-final-t-clause | |
13b8268e | 380 | (defun foo () (return (cond ((< 1 2) (bar "foo") (* 4 5)) ((= a b) (+ c d)) ((< 1 2 3 4 5) x) (t "foo")))) |
0949f072 | 381 | "function foo() { |
13b8268e | 382 | return 1 < 2 ? (bar('foo'), 4 * 5) : (a == b ? c + d : (1 < 2 < 3 < 4 < 5 ? x : 'foo')); |
0949f072 VS |
383 | }") |
384 | ||
385 | (test-ps-js cond-expression-middle-t-clause ;; should this signal a warning? | |
386 | (defun foo () (return (cond ((< 2 1) 5) (t "foo") ((< 1 2) "bar")))) | |
387 | "function foo() { | |
388 | return 2 < 1 ? 5 : 'foo'; | |
389 | }") | |
e5253c5b VS |
390 | |
391 | (test-ps-js funcall-if-expression | |
392 | (document.write | |
393 | (if (= *linkornot* 1) | |
394 | (ps-html ((:a :href "#" | |
395 | :onclick (lisp (ps-inline (transport)))) | |
396 | img)) | |
397 | img)) | |
398 | "document.write(LINKORNOT == 1 ? '<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">' + img + '</a>' : img)") | |
49c50da4 VS |
399 | |
400 | (test-ps-js negate-number-literal ;; ok, this was broken and fixed before, but no one bothered to add the test! | |
401 | (- 1) | |
402 | "-1") | |
efe35e12 VS |
403 | |
404 | (test macro-environment1 | |
405 | (is (string= (normalize-js-code (let ((macroname (gensym))) | |
406 | (ps* `(defmacro ,macroname (x) `(+ ,x 123)) | |
407 | `(defun test1 () | |
408 | (macrolet ((,macroname (x) `(aref data ,x))) | |
409 | (when (,macroname x) | |
410 | (setf (,macroname x) 123))))))) | |
411 | (normalize-js-code | |
412 | "function test1() { | |
413 | if (data[x]) { | |
414 | data[x] = 123; | |
415 | }; | |
416 | }; | |
417 | ")))) | |
921f2e02 VS |
418 | |
419 | (test-ps-js ampersand-whole-1 | |
420 | (macrolet ((foo (&whole foo bar baz) | |
421 | (declare (ignore bar baz)) | |
422 | (format nil "~a" foo))) | |
423 | (foo 1 2)) | |
424 | "'(FOO 1 2)';") |