2

I am trying to override (monkey patch) the it() function of the Jasmine framework and in the process, I want to know if a function which is passed as the second argument to the it() function is of type async or not. I tried using the instanceof Promise because all async functions return a promise, but it never resolves to true and it never goes into the if block in the following code block. I have tried logging all the functions to the console and I found that all the async() function specs have a return type of tslib_1.awaiter(some args..).

Here is what I have:

let newIt = jasmine.getEnv().it;
jasmine.getEnv().it = function(...args): jasmine.Spec {
  // do something.
 if(args[1] instanceOf Promise) {
   debugger; // never comes in here.
   // catch error.
 }
 return newIt.apply(this, arguments);
}

What am I doing wrong here? Could anyone please point me into a right direction?

Thank you.

Edit: Let's say I have the following two dummy specs, one is async and the other is synchronous:

Async Test:

const exp = () => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(1);
                }, 500);
            });
        };

 it('asyncTest', async () => {
      expect(await exp()).toEqual(1);
  }, 600);

Synchronous:

it('testNon', () => {
     expect(true).toBe(true);
 });
Tums
  • 559
  • 2
  • 5
  • 15

2 Answers2

2

This is XY problem. As pointed in this answer there is usually no need to check that it's async function because this doesn't matter. async function is just a function that returns a promise. Both () => Promise.resolve() and async () => {} should be treated in same way.

This is confirmed by the fact that it's impossible to distinguish between async and regular function in transpiled TypeScript code - both of them are just regular functions.

args[1] instanceOf Promise is incorrect, because a function won't be an instance of Promise. Monkey-patching is generally performed this way (this isn't specific to Jasmine or this case):

let newIt = jasmine.getEnv().it;
jasmine.getEnv().it = function(...args): jasmine.Spec {
  const fn = args[1];
  let newFn;
  if (fn.length > 0) {
    // async spec with done param
    newFn = function (done) {
      const result = fn.call(this, done);
      if(result instanceOf Promise)
      // or even better,
      // if(result && typeof result.then === 'function')
        debugger;
      return result;
    }
  } else {
    newFn = function () {
      const result = fn.call(this);
      if(result instanceOf Promise)
        debugger;          
      return result;
    }
  }

  args[1] = newFn;
  return newIt.apply(this, args);;
}

It's test function result that should be checked to be an instance of Promise (or to be checked if it's thenable).

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks a lot for clearly explaining it and pointing out the correct way of the monkey patching. It's weird that even now, the condition never becomes true and never hits the debugger. – Tums Mar 05 '18 at 19:28
  • Sorry, monkey patching went wrong there. Updated the code, guess this should do the trick. – Estus Flask Mar 05 '18 at 20:31
  • Sorry for taking too long to accept your answer. You rock. Thanks a ton. :) – Tums Mar 07 '18 at 21:54
0

This isn't working because the async functions always return a promise, but they aren't in themselves promises.

The simple answer is to look at the constructor of the function in question (but I'll explain why you shouldn't after):

const isAsync = func.constructor.name === 'AsyncFunction';

The problem with this approach is that async functions aren't really meant to be distinguishable from regular functions which return a promise. It would be better to use Promise.resolve to turn the result of any function call into a Promise, and await that.

qubyte
  • 17,558
  • 3
  • 30
  • 32
  • could you please give me a simple example how I could implement this in my case? I have updated my code with a couple of simple `it` specs which can be used in the scenario. – Tums Mar 02 '18 at 21:47
  • Are you saying, I should do something like this: `let x = Promise.resolve(args[1]);` And then check `if(p instanceof Promise){ // do something}` ? I didn't quite get the `await` part. – Tums Mar 02 '18 at 22:33