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

Double File Component scenario working unexpectedly well #188

Open
IlCallo opened this issue Jul 18, 2019 · 3 comments
Open

Double File Component scenario working unexpectedly well #188

IlCallo opened this issue Jul 18, 2019 · 3 comments
Labels

Comments

@IlCallo
Copy link

IlCallo commented Jul 18, 2019

Just highlighing a funny side effect of this library.

Context

I'm currently trying to make Vue + TypeScript + Jest work nice together in order to integrate it into Quasar framework (happy if you want to check the "upgrade guide" I put together and help with TODO stuff).

Funny discovery

While doing so, I came to the conclusion that currently the only way of getting component types into tests was to extract TS file into a separate file with the same name as the Vue one, then import both the .vue (shimmed) and the .ts (for typings) into the test file.

import QBtnDemo from './demo/QBtn-demo';
import QBtnDemoSfc from './demo/QBtn-demo.vue';

const wrapper = mount(QBtnDemoSfc as typeof QBtnDemo);
const vm = wrapper.vm;

This work pretty well, but it's kinda verbose.
At some point I had the random idea of directly mount the TS import just for fun and... It worked?! Structure-related tests passed, a couple of console.log assured me that template was being rendered, even if the TS file actually didn't have any info about it.

To make it more explicit, this works with correct component typings.

import QBtnDemo from './demo/QBtn-demo';

const wrapper = mount(QBtnDemo);
const vm = wrapper.vm;

If someone else can try this and give feedback, both positive and negative, it would be a good idea.

Possible explanation

If you are like me, you probably are wondering where does this black magic come from.
Actually I'm not really sure myself, but this is the best I could come up, helped by Quasar staff who knows TS and Jest, if someone can confirm or correct the hypotesis, I'd be happy to update.

ts-jest transpile at runtime .ts files into .js files (I can guess, with the same name of the TS ones).
At the same time, vue-jest transpile at runtime .vue files into .js files with all info attached (TS, template, style), always with the same name.

Because they write a file with the same name, I guess vue-jest is actually overwriting the TS-only transpilation with a full SFC implementation transpilation.

The import is done without the file extension, so we have import QBtnDemo from './demo/qbtn-demo'; for the TS file.
At compile/development time it will point to the TS file and give us correct typings for Jest to use upon mounting (and it extends Vue, so it won't complain), which is also compatible with ESLint and Vetur support.
At test time, instead, it will resolve to the Vue-transpiled JS file, which also contains info about the template and style of the component.

This comment can be useful to understand what's going on.

Requirements

  • SFC should be transformed into a "Double File Component" (DFC?) composed of a .vue and .ts file
  • Both files should be in the same folder
  • You should use vue-jest to process Vue files
  • You should use ts-jest to process TS files

Pros

  • You get full component typings with a neat syntax
  • It works seamlessly with Vetur

Cons

Other attempts

Previous to this discovery, I was experimenting with wildcard ambient module declarations to obtain typings directly when importing .vue files, but got some "domain collision" problems with the general Vue shim. If anyone can help on that issue, it's much appreciated.
Mostly because making that one work, although it needs some kind of automatic shim generation, is more future proof for when someone will find a way to make .vue files work semlessly and the DFC hack can be dropped.

@robertpiosik
Copy link

robertpiosik commented Apr 20, 2020

Your imported class extends Vue so obviously you can pass it to the mount function and get the right vm although

a couple of console.log assured me that template was being rendered, even if the TS file actually didn't have any info about it.

is not true, template hasn't been rendered and you can check it properly by using, for instance, html() method on the wrapper.

There is some ongoing work on TS plugin to make .vue files be treated as modules

@IlCallo
Copy link
Author

IlCallo commented Apr 28, 2020

@robertpiosik have you actually tried it?
Cause I just dug deeper: Jest module resolver at some point uses the provided module name without extension and, building it up using the configuration option moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'] I provided, resolves it to the right Vue file (I guess because vue extension is the first one in the array).

Right now I made some changes and broke it, but if I manage to make it work again this a viable solution to solve the problem I pointed out (making it work for Jest and get the right types for TS)

Edit: fixed, you can find the needed setup here

@sschneider-ihre-pvs
Copy link

maybe the easy way out for SFC, also a cheap dirty trick



// jest.config.js
module.exports = {
  // [...]
  globals: {
    'ts-jest': {
      diagnostics: {
        ignoreCodes: [2590]
      }
    }
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants