vuejs / vue-cli

🛠️ webpack-based tooling for Vue.js Development
https://cli.vuejs.org/
MIT License
29.76k stars 6.33k forks source link

Starter unit test fails when creating a Vue 3 project with TypeScript and Jest #5714

Closed adarean5 closed 4 years ago

adarean5 commented 4 years ago

Version

4.5.0

Reproduction link

https://github.com/adarean5/vue3-ts-jest

Environment info

System:
    OS: Windows 10 10.0.19041
    CPU: (12) x64 AMD Ryzen 5 3600 6-Core Processor
  Binaries:
    Node: 12.18.3 - C:\Program Files\nodejs\node.EXE
    Yarn: Not Found
    npm: 6.14.6 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 84.0.4147.89
    Edge: Spartan (44.19041.1.0), Chromium (84.0.522.40)
  npmGlobalPackages:
    @vue/cli: Not Found

Steps to reproduce

Run the test:unit script

What is expected?

The starter unit test (/tests/unit/example.spec.ts) should pass with output similar to:

 PASS  tests/unit/example.spec.ts
  HelloWorld.vue
    √ renders props.msg when passed (16ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.673s
Ran all test suites.

Process finished with exit code 0

What is actually happening?

Running test:unit produces the following:

 FAIL  tests/unit/example.spec.ts
  ● Test suite failed to run

    C:\Users\Jernej\Documents\Dev_playground\vue3-ts-jest\src\components\HelloWorld.vue:19
    import { toDisplayString as _toDisplayString, createVNode as _createVNode, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createBlock as _createBlock
 } from "vue";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import { shallowMount } from "@vue/test-utils";
    > 2 | import HelloWorld from "@/components/HelloWorld.vue";
        | ^
      3 | 
      4 | describe("HelloWorld.vue", () => {
      5 |   it("renders props.msg when passed", () => {

      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:537:17)
      at Object.<anonymous> (tests/unit/example.spec.ts:2:1)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        3.456s
Ran all test suites.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! vue3-ts-jest@0.1.0 test:unit: `vue-cli-service test:unit`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the vue3-ts-jest@0.1.0 test:unit script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Jernej\AppData\Roaming\npm-cache\_logs\2020-07-25T14_46_08_108Z-debug.log

Process finished with exit code 1

This project was created with the following options selected:

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, PWA, Router, CSS Pre-processors, Linter, Unit
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save, Lint and fix on commit
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files

I tried creating a new project with the same settings but with either Vue version 2 or JavaScript instead of TypeScript and they both seem to work.

I tried to apply fixes from what seems like a related issue (https://github.com/vuejs/vue-cli/issues/1584) but sadly none of them worked.

haoqunjiang commented 4 years ago

/cc @lmiller1990 @JessicaSachs

I'm not sure but it looks like a vue-jest bug.

lmiller1990 commented 4 years ago

Hm, vue-jest, the latest version of test utils and TS definitely do work together, I made a repo that illustrates this here.

I was able to reproduce this following the above steps. I then created a new project and chose not to use babel alongside TS and it worked fine. I think the problem is related to the babel transpilation running after the TS compilation step (maybe the @vue/cli-plugin-typecript-babel-preset)?

In the past, when I saw this error (but in a different context, not a vue-cli project, but I was using jest), I had to provide the @babel/preset-env to with babel get everything working. :thinking:

If you just want everything to work @adarean5, you can install the awesome ts-jest preset and setting your preset to ts-jest in jest.config.js. This worked for me.

I wonder if we can just use ts-jest as the preset by default in the Jest cli plugin. @sodatea what do you think? The ts-jest preset seems to work great for both TS and regular JS.

haoqunjiang commented 4 years ago

@vue/cli-plugin-babel/preset do have enabled module transpilation though.

https://github.com/vuejs/vue-cli/blob/f99079cbb053d539a4e4048770a8a4154407298a/packages/%40vue/cli-plugin-unit-jest/index.js#L13-L15 https://github.com/vuejs/vue-cli/blob/f99079cbb053d539a4e4048770a8a4154407298a/packages/%40vue/babel-preset-app/index.js#L221-L227

I don't know how to debug a Jest transformer but it seems vue-jest failed to pick up this babel config.

lmiller1990 commented 4 years ago

It does seem that way. Maybe finding the babel config is not working in the latest version of vue-jest (5.0.0-alpha something). It has not been tested very thoroughly yet, except when using ts-jest as the preset.

What do you think about using ts-jest as the default preset (at least for TS users?) This seems to make sense.

haoqunjiang commented 4 years ago

I think the bug may reside here https://github.com/vuejs/vue-jest/blob/e0cbca7920e3afca3bd333377a4e463b57631e66/lib/transformers/typescript.js#L29-L45

A babel config was found so no inline options were applied, but the project babel config did not take effect either.

haoqunjiang commented 4 years ago

We have both typescript and typescript-and-babel presets https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest/presets

Babel was required in many circumstances because of the need for modern mode, auto polyfill injection, and JSX, etc.

lmiller1990 commented 4 years ago

Hm, I wonder why this wasn't a problem in the past. That part of vue-jest has not changed. The new SFC compiler, @vue/compiler-sfc, transforms to import/export syntax - perhaps the old one, vue-template-compiler was transforming to require syntax, so that is why no-one noticed.

I made an issue in vue-jest to also track this: https://github.com/vuejs/vue-jest/issues/258

lmiller1990 commented 4 years ago

Interestingly enough digging into the preset we are using, if you commented out babelConfig: true it solves the problem, so the issue is definitely somewhere in vue-jest relating to babelConfig.

Actually, maybe not: the babelConfig option is a ts-jest thing. 🤔

I will continue investigating. Maybe ts-jest master @ahnpnl has some pointers on exactly the babelConfig: true option does in ts-jest?

adarean5 commented 4 years ago

Hm, vue-jest, the latest version of test utils and TS definitely do work together, I made a repo that illustrates this here.

I was able to reproduce this following the above steps. I then created a new project and chose not to use babel alongside TS and it worked fine. I think the problem is related to the babel transpilation running after the TS compilation step (maybe the @vue/cli-plugin-typecript-babel-preset)?

In the past, when I saw this error (but in a different context, not a vue-cli project, but I was using jest), I had to provide the @babel/preset-env to with babel get everything working. 🤔

If you just want everything to work @adarean5, you can install the awesome ts-jest preset and setting your preset to ts-jest in jest.config.js. This worked for me.

I wonder if we can just use ts-jest as the preset by default in the Jest cli plugin. @sodatea what do you think? The ts-jest preset seems to work great for both TS and regular JS.

Thank you for the reply and for providing a temporary solution. The issue might also be related to https://github.com/vuejs/vue-cli/issues/5712 . I remember getting that same message when running the start command.

lefreet commented 4 years ago

a mistake when install @vue/cli-plugin-unit-jest@4.5.0

▶ npm i @vue/cli-plugin-unit-jest@4.5.0
npm ERR! code ETARGET
npm ERR! notarget No matching version found for @vue/cli-plugin-unit-jest@4.5.0.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/shun/.npm/_logs/2020-07-29T06_19_36_580Z-debug.log

somethine wrong when publish ?

@sodatea

lmiller1990 commented 4 years ago

https://github.com/vuejs/vue-cli/blob/d319007ffa702986e68222f8ca07a59bbfc1dbd8/packages/%40vue/cli-plugin-unit-jest/package.json#L3

4.5.0 definitely exists.

image
haoqunjiang commented 4 years ago

@lefreet If you are using the taobao registry mirror, please switch back to the official one. Seems its synchronization with the upstream registry is broken, I can't get the latest version synced back https://developer.aliyun.com/mirror/npm/package/@vue/cli-plugin-unit-jest

haoqunjiang commented 4 years ago

OK, finally succeeded… Please try again.

lefreet commented 4 years ago

@sodatea all right, thanks.

ahnpnl commented 4 years ago

Interestingly enough digging into the preset we are using, if you commented out babelConfig: true it solves the problem, so the issue is definitely somewhere in vue-jest relating to babelConfig.

Actually, maybe not: the babelConfig option is a ts-jest thing. 🤔

I will continue investigating. Maybe ts-jest master @ahnpnl has some pointers on exactly the babelConfig: true option does in ts-jest?

babelConfig: true for ts-jest means:

You can view documentation about babelConfig at https://kulshekhar.github.io/ts-jest/user/config/babelConfig . It already contains all explanations.

lmiller1990 commented 4 years ago

Thanks, I should have read the docs 🤦

Anyway, I have found a fix. It looks like in Vue 2.x, the SFC tool (vue-template-compiler) would compile to commonjs. Vue 3.x compiler, @vue/compiler-sfc, does not - it compiles to ES modules. The error in the first post of this issue is actually the compiled template.

The fix is to compile the template using babel. I was using TypeScript's transpileModule https://github.com/vuejs/vue-jest/blob/e0cbca7920e3afca3bd333377a4e463b57631e66/lib/process.js#L52, see here, which likely is using the tsconfig.json, which is set to module: esnext. Instead, I will just use babel to compile the template (the template is entirely generated by @vue/compiler-sfc, so we don't really care about types here anyway).

I will ping @sodatea when I have fixed this and we can coordinate to include the latest vue-jest for the cli-plugin.

lmiller1990 commented 4 years ago

Ok, fixed here: https://github.com/vuejs/vue-jest/pull/260, I also explained how I tested it (basically reproduce this issue, change to my fixed vue-jest, ensure test passes).

Will leave for a day or two so people can review if they want, then release a new version of vue-jest (5.x) this weekend.

lmiller1990 commented 4 years ago

I released vue-jest@5.0.0-alpha.2. This should hopefully fix it. Can you try yarn add vue-jest@5.0.0-alpha.2 @adarean5 and see if this works for you now?

We should update the CLI plugin to use the latest @vue/test-utils (2.0.0-beta.1) and vue-jest (5.0.0-alpha.2).

cexbrayat commented 4 years ago

@lmiller1990 Bad news: I did not have this issue previously (as I don't use Babel alongside TS), but I'm now encountering it with vue-jest@5.0.0-alpha.2

Repro:

vue create vue-jest-alpha-2 --inlinePreset '{"useConfigFiles": true,"plugins": {"@vue/cli-plugin-typescript": {"classComponent": false},"@vue/cli-plugin-unit-jest": {}},"vueVersion": "3"}'
cd vue-jest-alpha-2
yarn test:unit

throws:

import { toDisplayString as _toDisplayString, createVNode as _createVNode, createTextVNode as _createTextVNode, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

Open package.json and downgrade to vue-jest@5.0.0-alpha.1 and everything is fine.

lmiller1990 commented 4 years ago

Ok so it's working with babel-jest but not with ts-jest now? VTU and vue-jest is definitely working in all my projects (non cli) so I guess we have another conflict in the cli-plugin somehow.

Will investigate 👀

lmiller1990 commented 4 years ago

Problem is we are not compiling to cjs, but es modules (this import and export) and Jest doesn't know what to do, right?

Should vue-jest force cjs? I thought jest could figure out es modules via babel-jest? This is a classic "I don't understand ES modules in node.js environment" error. I guess if you opt out of babel, babel-jest is not included. But if the preset is then ts-jest, I wonder why Jest cannot figure it out?

This can likely be fixed either here (jest config to make jest understand es modules) or in vue-jest (compile to cjs modules instead of es modules, since it seems @vue/sfc-compiler targets es modules). I am not sure where is best. Anyone know the best practice for this? Is there even one (there are so few Jest pre-processors out there, I don't think there is a single way to do it).

haoqunjiang commented 4 years ago

Hmmm… It's very complicated.

Previously the tsconfig returned from ts-jest was used for compiling the template code. ts-jest would enforce commonjs target if no babelConfig was found https://github.com/kulshekhar/ts-jest/blob/6bdf3320abea4d004c6863110ce306fd137e2fd8/src/config/config-set.ts#L558-L562

So it worked for TS without babel.

After this change, only babel is used for compilation, because no babel config can be found in the project, the code remains unchanged.

So the correct fix is not to use babel for all projects, but still use TS for compilation, only add babel to the pipeline if babelConfig is set.

lmiller1990 commented 4 years ago

TS + Babel + all the various tooling is so complicated! I think I got it, though. I tested with:

  1. babel + ts
  2. ts (no babel)
  3. babel (no ts, just regular js)

All working. If you opt not to use babel, it won't work. I am not sure this workflow makes any sense, sfc-compiler spits out code using ES modules, which Jest cannot understand.

@sodatea quick review? https://github.com/vuejs/vue-jest/pull/264/files

@cexbrayat if you want to test it out, grab the latest vue-jest repo and do cp -r vue-jest/lib <your_vue_cli_project>/node_modules/vue-jest and do yarn test:unit --no-cache (make sure to do --no-cache).

Can release once we are happy with it. I am sure there is a better way to write a jest transformer but better to get it working so people can try out Vue 3 and testing then optimize from there.

cexbrayat commented 4 years ago

@lmiller1990 I gave it a quick try with the repro I had, and your PR seems to work 🎉

lmiller1990 commented 4 years ago

Great, I will leave this for 24h-48h or so, so if anyone else has feedback (maybe Soda) they get a chance and release this weekend.

lmiller1990 commented 4 years ago

I released vue-jest 5.0.0-alpha.3. It is also available as vue-jest@next. This should hopefully resolve this!

cexbrayat commented 4 years ago

@lmiller1990 I confirm that our builds succeed with alpha.3 (TS without babel), thank you 🎉

haoqunjiang commented 4 years ago

Now that it seems to have been fixed. I'm closing this issue now.