1

I am using the jsonapi-serializer library to deserialize API data. I promisified the callback using angular's $q constructor and wrapped that in a service, this works fine on the browser, but when I test it using jasmine on the karma runner, the promise doesn't resolve. This is the method on the service (Notice I'm using TypeScript)

public deserialize(type: string, data: any): any {

// get the predefined options for resource type
let deserializeOpts: any = this.deserializeOpts[type];

// use jsonapi-serializer
// the options are not working
let deserializer: any = new JAS.Deserializer({});

console.log(data);
// return a promise with the parsed object
return this._$q((resolve: any, reject: any) => {
  deserializer.deserialize(data, (err: any, result: any) => {
    if (result) {
      console.log(resolve);
      resolve(result);
    } else {
      console.log(err);
      reject(err);
    }
  });
});
}

This is my test after a while trying to debug it

it('should flatten jsonapi user', function (done) {
  var deserialized;
  JsonapiParser.deserialize(type, apiUser).then(
    (result) => {
      deserialized = result;
      expect(deserialized).toEqual(apiUser);
      done();
    }
  );
});

An this is a sample use of the mentioned deserializer service

// returns the promise so controller can display the errors
return this.$http.get(url)
  .then(
    (response: any) => {
      if (response.data.data.length !== 0) {// deserialize data
        return this._deserializer.deserialize('activities', response.data) // the deserializer service is called;
      } else { // throw an error if data is empty
        return this.$q.reject({ error: this.ACTIVITY.empty });
      }
    },
    () => {
      return this.$q.reject({ error: this.ACTIVITY.connectionError });
    }
  ).then(
    (deserialized: any) => { // data is copied to original list so it doesn't lose it's bindings
      angular.copy(deserialized, this.list); // the result from the deserializer is used
      console.log(deserialized);
      return this.list;
  });

This last block of code works fine when compiled and run on the browser. But the tests get timed out. If I log inside the deserialize method, I can see that the callback gets resolved, but the promise never seems to digest. If I place a $rootScope.$digest() after the call to resolve, the test works, but I don't want to hardcode that in there, especially since the code is working when deployed.

perezperret
  • 316
  • 2
  • 7

2 Answers2

2

You are close with $rootScope.$digest(), however instead of triggering the digest from the app code, trigger it from the test with $rootScope.$apply().

See Testing Promises and Service Testing.

  • I understand this is the proper way to to trigger the digest cycle, but it is not working in this case, I don't know if it has something to do with the way the $q constructor works – perezperret Apr 06 '16 at 01:16
  • Do you mean that triggering digest using `.$apply` instead of `.$digest` from within the test code does not resolve the promise under test? eg `deserialized = result; $rootScope.$apply(); expect(deserialized).toEq(apiUser);` – Jeremiah Heller Apr 06 '16 at 01:25
  • Yes, I've tried logging inside the `then`, it's not getting called. The promise only gets resolved if I call $digest after resolve inside the app code – perezperret Apr 06 '16 at 01:48
  • Sorry, I missed that; of course it won't resolve if the digest hasn't triggered! Try it with `$rootScope.$apply()` called outside of the resolver callback: eg `JsonapiParser.deserialize(...).then(...); $rootScope.$apply()`. – Jeremiah Heller Apr 06 '16 at 01:54
  • Yes, I have tried this several different ways, using async tests passing in `done` and using synchronous tests and the promise doesn't resolve – perezperret Apr 06 '16 at 02:13
0

this works fine on the browser

I can't believe that. You are never calling resolve! Instead of

console.log(resolve);

you need to use

console.log(result);
resolve(result);

Btw, the typical node-style callback promisification is using if (err), not if (result). Maybe you want if (err || !result).

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • You're absolutely right, I copied a "debugging version" where I had removed the call to resolve, I have edited the snippet to reflect this, thank you for the feedback on the promisification styling. – perezperret Apr 06 '16 at 01:01