# # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. # proc njs_test {body {opts ""}} { if {$opts eq ""} { spawn -nottycopy njs } else { eval spawn -nottycopy njs $opts } expect -re "interactive njs \\((njs|QuickJS):.*\r\n\r\n>> " set len [llength $body] for {set i 0} {$i < $len} {incr i} { set pair [lindex $body $i] send [lindex $pair 0] expect [lindex $pair 1] } send "\n" send ".exit\r\n" expect eof } proc njs_run {opts expected_re} { catch {exec njs {*}$opts} out if {[regexp $expected_re $out match] == 0} { return -code error "njs_run: unexpected output '$out' vs '$expected_re'" } } njs_test { {"njs.version\r\n" "njs.version\r\n\*\.\*\.\*"} } # simple multi line interaction njs_test { {"var a = 1\r\n" "var a = 1\r\nundefined\r\n>> "} {"a *= 2\r\n" "a *= 2\r\n2\r\n>> "} } # function declarations in interactive mode njs_test { {"function a() { return 1; }\r\n" "undefined\r\n>> "} {"a();\r\n" "1\r\n>> "} {"function a() { return 2; }\r\n" "undefined\r\n>> "} {"a();\r\n" "2\r\n>> "} } # Global completions njs_test { {"\t\t" "$262*"} } # Global completions, single partial match njs_test { {"O\t" "O\a*bject"} } njs_test { {"Mat\t" "Mat\a*h"} } njs_test { {"conso\t" "conso\a*le"} } # Global completions, multiple partial match njs_test { {"cons\t\t" "console*const"} } njs_test { {"Type\t" "Type\a*Error"} {".\t\t" "TypeError.__proto__"} } njs_test { {"TypeError.\t\t" "TypeError.__proto__*TypeError.prototype"} } njs_test { {"Object.ge\t" "Object.ge\a*t"} {"\t\t" "Object.getOwnPropertyDescriptor*Object.getPrototypeOf"} } njs_test { {"JS\t" "JS\a*ON"} {".\t\t" "JSON.__*JSON.stringify"} } njs_test { {"decodeURI\t\t" "decodeURI*decodeURIComponent"} {"C\t" "decodeURIC\a*omponent"} } # Global completions, no matches njs_test { {"1.\t\t" "1."} } njs_test { {"1..\t\t" "1.."} } njs_test { {"'abc'.\t\t" "'abc'."} } # Global completions, global vars njs_test { {"var AA = 1; var AAA = 2\r\n" "var AA = 1; var AAA = 2\r\nundefined\r\n>> "} {"AA\t\t" "AA*AAA*"} } njs_test { {"var zz = 1\r\n" "var zz = 1\r\nundefined\r\n>> "} {"1 + z\t\r\n" "1 + z*z*\r\n2"} } njs_test { {"unknown_var\t\t" "unknown_var"} } njs_test { {"unknown_var.\t\t" "unknown_var."} } # An object's level completions njs_test { {"var o = {zz:1, zb:2}\r\n" "var o = {zz:1, zb:2}\r\nundefined\r\n>> "} {"o.z\t\t" "o.zb*o.zz"} } njs_test { {"var d = new Date()\r\n" "var d = new Date()\r\nundefined\r\n>> "} {"d.to\t\t" "d.toDateString*d.toLocaleDateString*d.toString"} } njs_test { {"var o = {a:new Date()}\r\n" "var o = {a:new Date()}\r\nundefined\r\n>> "} {"o.a.to\t\t" "o.a.toDateString*o.a.toLocaleDateString*o.a.toString"} } njs_test { {"var o = {a:1,b:2,333:'t'}\r\n" "var o = {a:1,b:2,333:'t'}\r\nundefined\r\n>> "} {"o.3\t\t" "o.3"} } njs_test { {"var a = Array(5000000); a.aab = 1; a.aac = 2\r\n" "var a = Array(5000000); a.aab = 1; a.aac = 2\r\n2\r\n>> "} {"a.\t\t" "a.aab*"} } njs_test { {"var a = new Uint8Array([5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n" "var a = new Uint8Array(\\\[5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n2\r\n>> "} {"a.\t\t" "a.aab*"} } # console object njs_test { {"console[Symbol.toStringTag]\r\n" "console\\\[Symbol.toStringTag]\r\n'Console'\r\n>> "} {"Object.prototype.toString.call(console)\r\n" "Object.prototype.toString.call(console)\r\n'\\\[object Console]'\r\n>> "} {"console.toString()\r\n" "console.toString()\r\n'\\\[object Console]'\r\n>> "} } # console log functions njs_test { {"console.log.length\r\n" "console.log.length\r\n0"} {"console.log()\r\n" "console.log()\r\nundefined\r\n>> "} {"console.log('')\r\n" "console.log('')\r\n\r\nundefined\r\n>> "} {"console.log(1)\r\n" "console.log(1)\r\n1\r\nundefined\r\n>> "} {"console.log(1, 'a')\r\n" "console.log(1, 'a')\r\n1\r\na\r\nundefined\r\n>> "} {"print(1, 'a')\r\n" "print(1, 'a')\r\n1\r\na\r\nundefined\r\n>> "} {"console.log('\\tабв\\nгд')\r\n" "console.log('\\\\tабв\\\\nгд')\r\n\tабв\r\nгд\r\nundefined\r\n>> "} {"console.error(42)\r\n" "console.error(42)\r\nE: 42\r\nundefined\r\n>> "} {"console.info(23)\r\n" "console.info(23)\r\n23\r\nundefined\r\n>> "} {"console.warn(37)\r\n" "console.warn(37)\r\nW: 37\r\nundefined\r\n>> "} } # console.time* functions njs_test { {"console.time()\r\n" "console.time()\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\ndefault: *.*ms\r\nundefined\r\n>> "} {"console.time(undefined)\r\n" "console.time(undefined)\r\nundefined\r\n>> "} {"console.timeEnd(undefined)\r\n" "console.timeEnd(undefined)\r\ndefault: *.*ms\r\nundefined\r\n>> "} {"console.time('abc')\r\n" "console.time('abc')\r\nundefined\r\n>> "} {"console.time('abc')\r\n" "console.time('abc')\r\nTimer \"abc\" already exists.\r\nundefined\r\n>> "} {"console.timeEnd('abc')\r\n" "console.timeEnd('abc')\r\nabc: *.*ms\r\nundefined\r\n>> "} {"console.time(true)\r\n" "console.time(true)\r\nundefined\r\n>> "} {"console.timeEnd(true)\r\n" "console.timeEnd(true)\r\ntrue: *.*ms\r\nundefined\r\n>> "} {"console.time(42)\r\n" "console.time(42)\r\nundefined\r\n>> "} {"console.timeEnd(42)\r\n" "console.timeEnd(42)\r\n42: *.*ms\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\nTimer \"default\" doesn’t exist."} {"console.timeEnd('abc')\r\n" "console.timeEnd('abc')\r\nTimer \"abc\" doesn’t exist."} {"console.time('abc')\r\n" "console.time('abc')\r\nundefined\r\n>> "} } njs_test { {"var print = console.log.bind(console); print(1, 'a', [1, 2])\r\n" "1\r\na\r\n*1,2*\r\nundefined\r\n>> "} {"var print = console.log.bind(console); print(console.a.a)\r\n" "TypeError: cannot * property *a* of undefined*"} {"print(console.a.a)\r\n" "TypeError: cannot * property *a* of undefined*"} } # Backtraces are reset between invocations njs_test { {"JSON.parse(Error())\r\n" "JSON.parse(Error())\r\nThrown:\r\nSyntaxError: *nexpected token"} {"console.a.a\r\n" "TypeError: cannot * property *a* of undefined*"} } njs_test { {"try { console.log({ toString: function() { throw 'test'; } }) } catch (e) {}\r\n" "undefined"} {"function f() { throw 't' }; try { console.log({ toString: function() { return f() } }) } catch (e) {}\r\n" "undefined"} } njs_test { {"(function() { throw 'test' })()\r\n" "Thrown:\r\n*test"} } njs_test { {"function f() { return ({}.a.a); }\r\n" "undefined\r\n>> "} {"var e; try {f()} catch (ee) {e = ee}; undefined\r\n" "undefined\r\n>> "} {"Object.keys(null)\r\n" "Thrown:\r\nTypeError: *annot convert*to object"} {"e\r\n" "TypeError: cannot * property *a* of undefined"} } # Non-ASCII characters njs_test { {"'絵文字'\r\n" "'絵文字'"} {"var v = 'абвгдеёжзийкл';v[10]\r\n" "'й'"} } # Immediate events njs_test { {"var t = setImmediate(console.log, 'a', 'aa')\r\n" "undefined\r\na\r\naa"} } njs_test { {"var a = 1 + 1; setTimeout(function (x) {a = x}, 0, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"setTimeout(function () {}, 1, 'a')\r\n" "InternalError: njs_set_timer(): async timers unsupported"} } njs_test { {"var a = 1 + 1; setTimeout(function (x) { setTimeout(function (y) {a = y}, 0, x)}, 0, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"var a = 1 + 1; setImmediate(function (x) { setImmediate(function (y) {a = y}, x)}, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"var i = 0; (function x() { if (i < 10) setImmediate(x); i++; })()\r\n" "undefined"} {"i\r\n" "i\r\n11"} } njs_test { {"var a = 0, t = setImmediate(function() {a = 1}); clearTimeout(t)\r\n" "undefined"} {"a\r\n" "a\r\n0"} } njs_test { {"var i = 0; (function x() { if (i < 3) setImmediate(x); i++; throw 'Oops';})()\r\n" "Oops"} {"i\r\n" "i\r\n4"} } njs_test { {"var i = 0, queue = []; (function x() { if (i < 5) setImmediate(x); queue.push(i++); })()\r\n" "undefined"} {"queue.toString()\r\n" "queue.toString()\r\n'0,1,2,3,4,5'"} } njs_run {"-c" "setTimeout(() => {console.log('A'.repeat(1024))}, 0); ref"} \ "^Thrown: ReferenceError: .* is not defined.*" njs_run {"-c" "setTimeout(() => {ref}, 0); setTimeout(() => {console.log('A'.repeat(1024))}, 0)"} \ "^Thrown: ReferenceError: .* is not defined.*" njs_test { {"setImmediate(() => { console.log('x'); return Promise.reject('xx'); })\r\n" "0\r\nx\r\nThrown:\r\n*Error: unhandled promise rejection: xx\r\n"} {"setImmediate(() => { console.log('x'); return Promise.reject('xx'); })\r\n" "1\r\nx\r\nThrown:\r\n*Error: unhandled promise rejection: xx\r\n"} {"42\r\n" "42\r\n"} } # CLI OPTIONS # help njs_run {"-h"} "Options" # command njs_run {"-c" "console.log(\"a b c\")"} "a b c" njs_run {"-c" "console.log("} "SyntaxError: \[Uu\]nexpected" # process njs_run {"-c" "console.log(typeof process.argv)"} "object" njs_run {"-c" "console.log(process.argv.slice(2))" "AAA"} "AAA" njs_run {"-c" "console.log(typeof process.env)"} "object" njs_run {"-c" "console.log(process.env.HOME != undefined)"} "true" njs_run {"-c" "console.log(process.env.___UNDECLARED != undefined)"} "false" njs_run {"-c" "console.log(process.pid)"} "\\d+" njs_run {"-c" "console.log(process.ppid)"} "\\d+" njs_run {"-c" "console.log(process.kill(process.pid, 0))"} "true" njs_run {"-c" "console.log(process.kill(process.pid, 'SIGCHLD'))"} "true" # script args njs_run {"test/script_args.js" "A" "B"} "AB" # version njs_run {"-v"} "\\d+\.\\d+\.\\d+"