power-assert icon indicating copy to clipboard operation
power-assert copied to clipboard

More descriptive results for `typeof` expressions

Open ELLIOTTCABLE opened this issue 9 years ago • 5 comments

So, something like this is a pretty common pattern:

assert(typeof wrap === 'function')

    AssertionError:   # giraphe.es6.js:61

assert(typeof wrap === 'function')
       |           |
       "object"    false

--- [string] 'function'
+++ [string] typeof wrap
@@ -1,8 +1,6 @@
-function
+object


    + expected - actual

    -false
    +true

Although this works, I feel that this could easily be extended with more useful information: for instance, in most failing assertions, there's information about the object under test; but specifically in typeof's case, that gets lost, and it gets treated as a simple comparison of two static strings.

Here's something like what I'd expect to see (hastily cobbled together from the results for assert(wrap === 'function'), because it was an easy change :P:

    AssertionError:   # giraphe.es6.js:61

assert(typeof wrap === 'function')
       |      |    |
       |      |    false
       |      Object{}
       "object"

[string] 'function'
=> "function"
[Object] wrap
=> Object{}

--- [string] 'function'
+++ [string] typeof wrap
@@ -1,8 +1,6 @@
-function
+object


    + expected - actual

    -false
    +true

ELLIOTTCABLE avatar May 28 '16 13:05 ELLIOTTCABLE

Thank you for your question.

Unfortunately, it's a limitation of current implementation of power-assert. I cannot support typeof UnaryExpression since it potentially causes ReferenceError.

Let me explain. Given example.js,

var obj = {
    name: 'foo'
};

function capt (val) {
    return val;
}

console.log(typeof obj);  //=> 'object'
console.log(typeof capt(obj));  //=> 'object'

console.log(typeof doesNotExist);  //=> 'undefined'
console.log(typeof capt(doesNotExist));  //=> ReferenceError: doesNotExist is not defined

results in ReferenceError when executed. Since typeof operator can take variable that does not defined.

$ node explain.js
object
object
undefined
/Users/takuto/src/github.com/power-assert-js/power-assert/explain.js:13
console.log(typeof capt(doesNotExist));  //=> ReferenceError: doesNotExist is not defined
                        ^

ReferenceError: doesNotExist is not defined
    at Object.<anonymous> (/Users/takuto/src/github.com/power-assert-js/power-assert/explain.js:13:25)
    at Module._compile (module.js:541:32)
    at Object.Module._extensions..js (module.js:550:10)
    at Module.load (module.js:456:32)
    at tryModuleLoad (module.js:415:12)
    at Function.Module._load (module.js:407:3)
    at Function.Module.runMain (module.js:575:10)
    at startup (node.js:160:18)
    at node.js:445:3
$

capt function is the simplest mimic of what power-assert is doing internally, so when I tried to capture typeof UnaryExpression like typeof foo, it always result in ReferenceError.

Since test tool should not change behavior of SUT (System Under Test), I gave up supporting typeof UnaryExpression.

Sorry for inconvenience. I'll write about this limitation to FAQ section in REAME. Thanks!

twada avatar Jun 10 '16 09:06 twada

Wait, tho: can't you special-case typeof at the syntactic level?

i.e. anything else in JS becomes expr(expr(expr)) to capt(expr(capt(expr(capt(expr))))) … of course, that makes sense; but typeof is special, so it makes sense to special-case it: expr(typeof expr)) to capt(expr(capt(typeof expr !== 'undefined' && typeof capt(expr) or something like that

Sorry for being terse, am on phone. Let me know if confused, can re-explain when at computer! On Fri, Jun 10, 2016 at 4:08 AM Takuto Wada [email protected] wrote:

Thank you for your question.

Unfortunately, it's a limitation of current implementation of power-assert. I cannot support typeof UnaryExpression since it potentially causes ReferenceError.

Let me explain. Given example.js,

var obj = { name: 'foo' }; function capt (val) { return val; } console.log(typeof obj); //=> 'object'console.log(typeof capt(obj)); //=> 'object' console.log(typeof doesNotExist); //=> 'undefined'console.log(typeof capt(doesNotExist)); //=> ReferenceError: doesNotExist is not defined

results in ReferenceError when executed. Since typeof operator can take variable that does not defined.

$ node explain.js object object undefined /Users/takuto/src/github.com/power-assert-js/power-assert/explain.js:13 console.log(typeof capt(doesNotExist)); //=> ReferenceError: doesNotExist is not defined ^

ReferenceError: doesNotExist is not defined at Object. (/Users/takuto/src/github.com/power-assert-js/power-assert/explain.js:13:25) at Module._compile (module.js:541:32) at Object.Module._extensions..js (module.js:550:10) at Module.load (module.js:456:32) at tryModuleLoad (module.js:415:12) at Function.Module._load (module.js:407:3) at Function.Module.runMain (module.js:575:10) at startup (node.js:160:18) at node.js:445:3 $

capt function is what power-assert is doing internally, so when I tried to capture typeof UnaryExpression like typeof foo, it always result in ReferenceError.

Since test tool should not change behavior of SUT (System Under Test), I gave up supporting typeof UnaryExpression.

Sorry for inconvenience. I'll write about this limitation to FAQ section in REAME. Thanks!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/power-assert-js/power-assert/issues/60#issuecomment-225130851, or mute the thread https://github.com/notifications/unsubscribe/AAAAyLCpy5K5DD3F3w_aJSkHrQGOctSaks5qKSlrgaJpZM4IpEzA .

ELLIOTTCABLE avatar Jun 10 '16 11:06 ELLIOTTCABLE

Ah, immediately realized a problem with that, so pulled out my laptop after sending.

Here's the two solutions I see:

// foo( typeof bar )
var _t; capt(foo( (((_t = typeof bar) !== 'undefined' && capt(bar)), _t)))

That's fast, but the downside is that it evaluates bar twice; so it's only suitable for situations where bar is not a complicated expression: i.e. it's just a standalone identifier.

Now, I'm not convinced anything except a standalone identifier can trigger a ReferenceError, will need to check the spec, but if it can, then more complicated expressions can be transformed to:

// foo( typeof bar(widget widget widget) )
var _a, _b; _a = capt(widget widget widget); try { _b = capt_maybe(bar(_a)) }
    catch (e) { _b = capt_fail() }; capt(foo(_b))

… or something like that, I got bogged down halfway through trying to construct it.

I think I've said this elsewhere on this project, but just to iterate: I'm totally aware that this looks like a ton of work. Special-casing in the parser, special-casing in the generator, special-casing capturing-functions that may not be evaluated (in the case of the try/catch approach), it's all a mess. I do disagree with you in that I think it's possible, but “this is too much work right now, and is outside the scope of this project, I'm afraid” is an answer I'm prepared to accept. (=

ELLIOTTCABLE avatar Jun 10 '16 12:06 ELLIOTTCABLE

Resurrecting — any input on my suggestions, @twada? I wouldn't mind submitting a pull-request for one of my solutions (whichever you prefer), if this behaviour is welcome.

ELLIOTTCABLE avatar Jun 08 '17 00:06 ELLIOTTCABLE

@ELLIOTTCABLE Thank you for resurrecting this issue. This is in my someday task. Sorry for my late response. I think the solution might be look like babel's typeof helper. Could be a bit simpler.

twada avatar Jun 08 '17 01:06 twada