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)") |
dbb7017b | 265 | |
07afea38 VS |
266 | (test-ps-js operator-precedence |
267 | (* 3 (+ 4 5) 6) | |
268 | "3 * (4 + 5) * 6") | |
269 | ||
270 | (test-ps-js incf1 | |
271 | (incf foo bar) | |
272 | "foo += bar") | |
273 | ||
274 | (test-ps-js decf1 | |
275 | (decf foo bar) | |
276 | "foo -= bar") | |
277 | ||
278 | (test-ps-js setf-conditional | |
279 | (setf foo (if x 1 2)) | |
280 | "foo = x ? 1 : 2;") | |
281 | ||
282 | (test-ps-js obj-literal-numbers | |
283 | (create 1 "foo") | |
284 | "{ 1 : 'foo' }") | |
285 | ||
286 | (test-ps-js obj-literal-strings | |
287 | (create "foo" 2) | |
a805b30d VS |
288 | "{ 'foo' : 2 }") |
289 | ||
290 | (test-ps-js slot-value-string | |
291 | (slot-value foo "bar") | |
292 | "foo['bar']") | |
b44afd8f VS |
293 | |
294 | (test-ps-js slot-value-progn | |
13b8268e VS |
295 | (slot-value (progn (some-fun "abc") "123") "length") |
296 | "(someFun('abc'), '123')['length']") | |
b44afd8f VS |
297 | |
298 | (test-ps-js method-call-block | |
13b8268e VS |
299 | (.to-string (progn (some-fun "abc") "123")) |
300 | "(someFun('abc'), '123').toString()") | |
b44afd8f VS |
301 | |
302 | (test-ps-js create-blank | |
303 | (create) | |
304 | "{ }") | |
305 | ||
306 | (test-ps-js blank-object-literal | |
307 | {} | |
308 | "{ }") | |
44934751 VS |
309 | |
310 | (test-ps-js defun-rest1 | |
311 | (defun foo (&rest bar) (alert bar[1])) | |
312 | "function foo() { | |
313 | var bar = []; | |
2e593e4c VS |
314 | for (var _js2 = 0; _js2 < arguments.length - 0; _js2 = _js2 + 1) { |
315 | bar[_js2] = arguments[_js2 + 0]; | |
44934751 VS |
316 | }; |
317 | alert(bar[1]); | |
318 | }") | |
319 | ||
320 | (test-ps-js defun-rest2 | |
321 | (defun foo (baz &rest bar) (return (+ baz (aref bar 1)))) | |
2e593e4c | 322 | "function foo(baz) { |
44934751 | 323 | var bar = []; |
2e593e4c VS |
324 | for (var _js2 = 0; _js2 < arguments.length - 1; _js2 = _js2 + 1) { |
325 | bar[_js2] = arguments[_js2 + 1]; | |
44934751 VS |
326 | }; |
327 | return baz + bar[1]; | |
328 | }") | |
2e593e4c VS |
329 | |
330 | (test-ps-js defun-keyword1 | |
331 | (defun zoo (foo bar &key baz) (return (+ foo bar baz))) | |
332 | "function zoo(foo, bar, _js1) { | |
333 | _js1 = undefined === _js1 && { } || _js1; | |
334 | return foo + bar + _js1.baz; | |
335 | }") | |
b0f64e9b VS |
336 | |
337 | (test-ps-js cond1 | |
338 | (cond ((= x 1) 1)) | |
339 | "if (x == 1) { | |
340 | 1; | |
341 | }") | |
342 | ||
343 | (test-ps-js cond2 | |
13b8268e | 344 | (cond ((= x 1) 2) ((= y (* x 4)) (foo "blah") (* x y))) |
b0f64e9b VS |
345 | "if (x == 1) { |
346 | 2; | |
347 | } else if (y == x * 4) { | |
13b8268e | 348 | foo('blah'); |
b0f64e9b VS |
349 | x * y; |
350 | }") | |
5705b542 VS |
351 | |
352 | (test-ps-js if-exp-without-else-returns-null | |
353 | (return (if x 1)) | |
354 | "return x ? 1 : null") | |
355 | ||
356 | (test-ps-js progn-expression-single-statement | |
357 | (return (progn (* x y))) | |
358 | "return x * y") | |
0949f072 VS |
359 | |
360 | (test-ps-js cond-expression1 | |
13b8268e | 361 | (defun foo () (return (cond ((< 1 2) (bar "foo") (* 4 5))))) |
0949f072 | 362 | "function foo() { |
13b8268e | 363 | return 1 < 2 ? (bar('foo'), 4 * 5) : null; |
0949f072 VS |
364 | }") |
365 | ||
366 | (test-ps-js cond-expression2 | |
367 | (defun foo () (return (cond ((< 2 1) "foo") ((= 7 7) "bar")))) | |
368 | "function foo() { | |
369 | return 2 < 1 ? 'foo' : (7 == 7 ? 'bar' : null); | |
370 | }") | |
371 | ||
372 | (test-ps-js cond-expression-final-t-clause | |
13b8268e | 373 | (defun foo () (return (cond ((< 1 2) (bar "foo") (* 4 5)) ((= a b) (+ c d)) ((< 1 2 3 4 5) x) (t "foo")))) |
0949f072 | 374 | "function foo() { |
13b8268e | 375 | return 1 < 2 ? (bar('foo'), 4 * 5) : (a == b ? c + d : (1 < 2 < 3 < 4 < 5 ? x : 'foo')); |
0949f072 VS |
376 | }") |
377 | ||
378 | (test-ps-js cond-expression-middle-t-clause ;; should this signal a warning? | |
379 | (defun foo () (return (cond ((< 2 1) 5) (t "foo") ((< 1 2) "bar")))) | |
380 | "function foo() { | |
381 | return 2 < 1 ? 5 : 'foo'; | |
382 | }") | |
e5253c5b VS |
383 | |
384 | (test-ps-js funcall-if-expression | |
385 | (document.write | |
386 | (if (= *linkornot* 1) | |
387 | (ps-html ((:a :href "#" | |
388 | :onclick (lisp (ps-inline (transport)))) | |
389 | img)) | |
390 | img)) | |
391 | "document.write(LINKORNOT == 1 ? '<a href=\"#\" onclick=\"' + 'javascript:transport();' + '\">' + img + '</a>' : img)") |