Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prototype does not exist on mocked instances #1069

Closed
geekox86 opened this issue Apr 14, 2019 · 24 comments
Closed

prototype does not exist on mocked instances #1069

geekox86 opened this issue Apr 14, 2019 · 24 comments

Comments

@geekox86
Copy link

geekox86 commented Apr 14, 2019

Issue :

`
import { mocked } from 'ts-jest/utils'
import Vue from 'vue'

jest.mock('vue')

const MockedVue = mocked(Vue, true)

// MockedVue.prototype does not exist
`

Expected behavior :

MockedVue.prototype should exist

Debug log:

Error:(9, 11) TS2339: Property 'prototype' does not exist on type 'MockedObjectDeep<VueConstructor>'.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 16, 2019

hi, I can't reproduce your issue on a newly generated project by using Vue CLI. I have the project setup here. Maybe there is sth wrong in your project config ?

@geekox86
Copy link
Author

geekox86 commented Apr 16, 2019

Hi @ahnpnl, thank you for your response.

I am actually using the following versions:
"@types/jest": "^24.0.11"
"jest": "^24.7.1'"
"ts-jest": "^24.0.2"

If I use the following versions, then the problem disappear:
"@types/jest": "^23.3.14"
"jest": "^23.6.0"
"ts-jest": "^23.10.5"

Could you please upgrade your versions of @types/jest, jest, and ts-jest from v23 to v24 and try again. The problem is that my IDE does not recognize the existing of prototype field inside MockedVue although it actually exists.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 16, 2019

hmm, I just tried upgrading dependencies like yours but still couldn't reproduce the issue (updated master branch of the sample repo too).

@geekox86
Copy link
Author

Hi again, please check my repo
Note that I am not using Vue CLI. See if you can reproduce the problem in tests/index.ts file line 15 and 19. I really appreciate your help @ahnpnl. Thank you.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 16, 2019

ok I could reproduce the issue. Actually with CLI it doesn't work either when I wrote MockedVue.prototype.<something> .
I found out the typings for testing.ts has been changed due to #994 . Now mocked returns MockedObjectDeep<VueConstructor<Vue>> , previously it returned MockedObjectDeep<VueConstructor<HelloWorld>> (HelloWorld is project name)

In my opinion, the previous return type wasn't correct and the new return type actually makes more sense.

To fix your issue, I suggest:

import VueApolloGraphQLPlugin from '@/codes/index';
import { mocked } from 'ts-jest/utils';
import Vue from 'vue';

jest.mock('vue');

describe('Vue Apollo GraphQL Plugin', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('should extend Vue prototype with $apollo', () => {
    const MockedVueApolloGraphQLPlugin = mocked(VueApolloGraphQLPlugin, true);
    const MockedVue = mocked(Vue, true);

    expect(MockedVueApolloGraphQLPlugin.prototype.$apollo).toBeUndefined();

    MockedVue.use(VueApolloGraphQLPlugin, []);

    expect(MockedVueApolloGraphQLPlugin.prototype.$apollo).toBeDefined();
  });
});

Because $apollo actually exists on VueApolloGraphQLPlugin only, it doesn't exist on Vue. But then I think your test won't be valid anymore since it tried to test an extended prototype of Vue which includes $apollo

@geekox86
Copy link
Author

geekox86 commented Apr 16, 2019

Hi again. I have not finished the tests yet. I am only trying to make my IDE recognizes the existing of MockedVue.prototype as it actually exists in the original Vue. mocked should return a mock with prototype right?

@geekox86
Copy link
Author

geekox86 commented Apr 17, 2019

@ahnpnl VueApolloGraphQLPlugin when registered with Vue.use should add $apollo to Vue.prototype as VueApolloGraphQLPlugin is a plugin that extends Vue functionality.

Now what is your suggestion to make my IDE recognizes Vue.prototype in those tests?
Is this considered a bug in ts-jest or @types/jest ?

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 17, 2019

I think maybe it's typing issue from ts-jest util mocks, the source is here.

PR is welcome 👍

@geekox86
Copy link
Author

geekox86 commented Apr 17, 2019

I will try to look into it, but if you suspect a specific line in the typing that would help.

Currently, I am using // @ts-ignore on the unrecognized MockedVue.prototype to make it working without complains. However, I am also using babel-jest with:
"globals": { "ts-jest": { "babelConfig": true } }

in package.json and I'm getting the following error:

   Test suite failed to run

    /Users/geekox86/Documents/GitHub/vue-apollo-graphql/tests/index.ts:2
    import VueApolloGraphQLPlugin from '@/codes/index';
           ^^^^^^^^^^^^^^^^^^^^^^

    SyntaxError: Unexpected identifier

      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:471:17)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:513:25)

The moment I remove babelConfig or I use incline babel configuration it works!
I have updated my repo.

Any clue? :(

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 17, 2019

it is caused by your tsconfig.json set target to esnext, jest only runs with commonjs. To fix this, you need to create a tsconfig.spec.json somewhere in your workspace (perhaps right inside tests folder) and then specify ts-jest tsConfig to point to that file. How to config ts-jest can be found here.

Your tsconfig.spec.json could be like this

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "sourceMap": true
  },
  "exclude": [
    "node_modules"
  ]
}

Regarding to typing in testing.ts, I'm not sure which line but somewhere inside there...

@geekox86
Copy link
Author

geekox86 commented Apr 17, 2019

Thank you so much @ahnpnl, you are such a great help to me :)
It seems that inline tsConfig configurations inside package.json of ts-jest are merged with project tsconfig.json rather than being overwritten. Because I have tried to only change module to commonjs in the inline configurations and it did work. Also, changing configurations in project tsconfig.json also impacted the tests. Is my assumption about configuration merging correct?

I have updated the repo if you would like to take a look.

BTW, do I still need the following line in my configuration since I am using "babelConfig": true? rather do I need babel-jest as a dependency?
"^.+\\.(js|jsx)$": "babel-jest"

I will give testing.ts a try to debug the issue.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 17, 2019

I think the executed codes are around this. I can't remember if tsconfig is merged or overwritten but I think it should be overwritten. Perhaps if your testing is correct, then it is a bug.

Using babel is optional configuration. Some people want to transform js with babel and ts with ts-jest before running tests meanwhile some people don't and just leave everything to ts-jest to transform.

@geekox86
Copy link
Author

So are you saying that if I set babelConfg to true without installing babel-jest then ts-jest will transform JS using babel according to my babel configuration in package.json?

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 17, 2019

babel-jest is shipped together with jest so you don't have to explicitly install it. When you don't set babelConfig to true, ts-jest will transform js as well as ts. Set it to true, babel doesn't know it immediately but you have to add regex in transform property of jest config to say: if js files use babel, if ts file use ts-jest

@geekox86
Copy link
Author

geekox86 commented Apr 17, 2019

Very clear now. I quickly went over the code in config-set.ts and I noticed two things:

1- it seems that it intentionally merges tsconfig.json with tsConfig in package.json which I really like. If this is not intended then I would like to keep it as an option at least.

2- ts-jest actually sets module to esnext by default if target is not es3 nor es5. So in this case if I use it to deal with Babel transformation, then I should be able to use esnext modules again without the need for tsConfig right?

const defaultModule = [ts.ScriptTarget.ES3, ts.ScriptTarget.ES5].includes(target)
? ts.ModuleKind.CommonJS
: ts.ModuleKind.ESNext

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 17, 2019

I haven't used babel yet with ts-jest. But looking at Vue generated project, target is set to esnext and it has Babel config and the tests work so I think your thought is correct

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 22, 2019

There is a discussion about this util mock in #1048. I think jest team will take care of this. Perhaps you can raise your concern about this typing there.

@geekox86
Copy link
Author

Greetings @ahnpnl. I have a question for you :)

Is it possible to configure ts-jest to also use babel-jest after compiling .ts and .tsx files in order to apply some babel plugins?

Thank you in advance.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 26, 2019

you can check it here

@geekox86
Copy link
Author

geekox86 commented Apr 26, 2019

@ahnpnl, I have read the docs multiple times but it is still not clear to me. What is clear is that you can use ts-jest with js(x) and ts(x) or you can use it only for ts(x) and leave js(x) for babel-jest. However, it is not clear if I can use both ts-jest and babel-jest with ts(x) in one pipeline to get benefits from both. I have also tested this setup and extracted the generated code via my debugger, and it seems that ts-jest is not forwarding its output to babel-jest. The only way is to use babel-jest for everything (without ts-jest) and configure it to compile typescript code as well. I would like to still use ts-jest as it will always use latest version of tsc, rather than using babel plugins for this which may be outdated or lack the support for multiple TS language features. What do you think?

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 26, 2019

As far as I remember your case is not possible, couldn't remember where the discussion was.

cc @kulshekhar @GeeWee

@geekox86
Copy link
Author

geekox86 commented Apr 26, 2019

Hi again. What I am asking for is simply a way to chain ts-jest with babel-jest similar to how webpack loaders (ts-loader and babel-loader) are chained.

I would love to use webpack to resolve these issues, but cannot find a way.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Apr 26, 2019

according to this line look like babel-jest is invoked after typescript compiler. In addition to that, the doc also confirms.

In general ts-jest will invoke typescript compiler first to compile ts to js then hand over compiled js files to babel if users use babel. I don’t think ts-jest should change this order. Does this information help you ?

@ahnpnl
Copy link
Collaborator

ahnpnl commented Mar 24, 2020

close in favor of #1048

@ahnpnl ahnpnl closed this as completed Mar 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants