expression-globals-typescript icon indicating copy to clipboard operation
expression-globals-typescript copied to clipboard

ReferenceError: Cannot access 'thisLayer' before initialization

Open coeing opened this issue 4 years ago • 9 comments

Hi there,

Thanks for providing this package, it makes developing After Effects libraries with TypeScript much easier :)

Unfortunately I get an error when importing the package inside Jest unit tests:

ReferenceError: Cannot access 'thisLayer' before initialization

Having a look at your code I saw that you define thisLayer after the declaration of the Layer class, but thisLayer is also used to set the parent of a layer in the Layer constructor (line 851 in index.js). This seems to cause the error.

I am using your template create-expression-lib where Jest unit tests are included. In the index.test.ts file you are mocking this package, but from the docs this should not be necessary: https://github.com/motiondeveloper/create-expression-lib#testing

coeing avatar Jul 07 '21 09:07 coeing

If I do the same mocking in my Jest tests as in index.test.ts I can get rid of the error. But of course the mocked constructors only return undefined values, so my tested logic fails :/ Any hint on how this package should/can be used in Jest tests would be great :)

coeing avatar Jul 07 '21 09:07 coeing

Hey @coeing, thanks for bringing this up!

I encountered this a while ago, getting the exact error you did, but thought I had solved it with the current mocking strategy:

jest.mock('expression-globals-typescript', () => ({
  Layer: jest.fn(),
  Comp: jest.fn(),
  Property: jest.fn(),
}));

As you pointed out though, it looks like I only got rid of the error without fixing the issue 😅 My bad!

I used a different mocking solution in eKeys you can check out here: https://github.com/motiondeveloper/eKeys/blob/master/src/index.test.ts

Which seemed to work in that situation, but am not sure it's the way to go - if you're able to try it out and let me know if it works in your situation that would be great.

I'll take a look at getting testing working in the template setup and let you know what I find. I'm pretty new at testing and Jest so sorry for the confusion!

As you said:

Having a look at your code I saw that you define thisLayer after the declaration of the Layer class, but thisLayer is also used to set the parent of a layer in the Layer constructor (line 851 in index.js). This seems to cause the error.

Might be a good place to start. I always thought the error came from the thisLayer definition in index.ts but you might be right in that it comes from the lib - weird that typescript didn't error but Jest does on it.

Thanks!!

timhaywood avatar Jul 08 '21 04:07 timhaywood

I also realized it's worth clarifying (should do this in the readme too) - since this is only a mock implementation of the expressions API, it only mocks the API itself and not the implementation.

So while this is great for writing expressions, and testing your own code (like we do with ekeys), anything that relies on the expressions API outputs (for example wiggle, etc) won't be able to be tested with Jest, you'd have to do it in Ae.

That does give me the idea of getting Jest working within Ae though, which might be handy...

Otherwise I'd have to recreate the entire functionality of the API, which would be a lot 😬

Hopefully that makes sense, just wanted to clarify going forward.

timhaywood avatar Jul 08 '21 05:07 timhaywood

Fixed the use of thisLayer in the package source in the latest release, but not sure if it will fix it.

https://github.com/motiondeveloper/expression-globals-typescript/releases/tag/v3.2.2

timhaywood avatar Jul 08 '21 05:07 timhaywood

Hi @timhaywood ,

Thanks a lot for the quick response :)

I am no expert with Jest as well, but gathered some experience during my recent projects. As you say: Your package already mocks the objects that will be available in After Effects. So it should not be necessary to have a jest.mock call. I will try your version 3.2.2 and let you know if it works with that change :)

coeing avatar Jul 13 '21 11:07 coeing

Unfortunately your change breaks the Layer constructor completely because it creates an infinite loop by calling itself (this.parent = new Layer()). You can easily test it with this unit test:

describe("Layer", () => {
  describe("constructor", () => {
    it("constructor should create a new instance", () => {
      const layer = new Layer();
      expect(layer).not.toBeUndefined();
    });
  });
});

I would suggest to set the parent member to undefined initially. As far as I can see it this is also what happens in After Effects.

coeing avatar Jul 13 '21 11:07 coeing

Makes sense, thanks @coeing !

timhaywood avatar Jul 14 '21 01:07 timhaywood

@coeing Layer constructor should be fixed in the latest release: https://github.com/motiondeveloper/expression-globals-typescript/releases/tag/v3.2.3

Will get back onto tackling the actual testing issue again now, thanks for all your help! 👍

timhaywood avatar Jul 20 '21 03:07 timhaywood

@timhaywood Thanks, will try the new release soon :) Right now our After Effects scripts I use your library for are in an early phase and a first version is currently under review. But when I continue their development I might fork your package and send you some pull requests that might be useful for other devs, too :)

coeing avatar Jul 20 '21 07:07 coeing