-
Notifications
You must be signed in to change notification settings - Fork 65
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
Detect for typescript and use that install profile for jest / ava / etc. #48
Comments
As said on Discord channel, I'll write here updates needed to make Jest work together with Typescript. New Jest configuration
module.exports = {
globals: {
__DEV__: true,
},
setupFilesAfterEnv: ['<rootDir>/test/jest/jest.setup.ts'], // <== was a JS file, now is TS
// noStackTrace: true,
// bail: true,
// cache: false,
// verbose: true,
// watch: true,
collectCoverage: true,
coverageDirectory: '<rootDir>/test/jest/coverage',
collectCoverageFrom: [
'<rootDir>/src/**/*.vue',
'<rootDir>/src/**/*.js',
'<rootDir>/src/**/*.ts',
'<rootDir>/src/**/*.jsx',
],
coverageThreshold: {
global: {
// branches: 50,
// functions: 50,
// lines: 50,
// statements: 50
},
},
testMatch: [
// Matches tests in any subfolder of 'src' or into 'test/jest/__tests__'
// Matches all files with extension 'js', 'jsx', 'ts' and 'tsx'
'<rootDir>/test/jest/__tests__/**/*.(spec|test).+(ts|js)?(x)',
'<rootDir>/src/**/__tests__/*_jest.(spec|test).+(ts|js)?(x)',
],
moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'ts', 'tsx'],
moduleNameMapper: {
'^vue$': '<rootDir>/node_modules/vue/dist/vue.common.js',
'^test-utils$':
'<rootDir>/node_modules/@vue/test-utils/dist/vue-test-utils.js',
'^quasar$': '<rootDir>/node_modules/quasar/dist/quasar.common.js',
'^~/(.*)$': '<rootDir>/$1',
'^src/(.*)$': '<rootDir>/src/$1',
'.*css$': '<rootDir>/test/jest/utils/stub.css',
},
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
'.*\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
},
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!quasar/lang)',
],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
}; New TypeScript configuration
{
"compilerOptions": {
"allowJs": true,
"sourceMap": true,
"target": "es6",
"strict": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"baseUrl": "."
},
"exclude": ["node_modules"]
}
There are two options to extend TS capabilities to
Demo componentCurrently the only way to get typings into tests is to move from a SFC to a DFC (Double File Component 😁), which means that TS script content should be extracted in a TS file in the same folder of the SFC one and called the same, then referenced by the
import Vue from 'vue';
export default Vue.extend({ // <= MUST extend Vue instance
name: 'QBUTTON',
data: function(): { counter: number; input: string } { // <= data MUST have a return type or TS won't be able to correctly infer its content on `this` context later on
return {
counter: 0,
input: 'rocket muffin',
};
},
methods: {
increment(): void { // <= methods return type MUST be annotated too
this.counter++;
},
},
});
<script lang="ts" src="./QBtn-demo.ts"></script>
<template>
<div>
<p class="textContent">{{ input }}</p>
<span>{{ counter }}</span>
<q-btn id="mybutton" @click="increment()"></q-btn>
</div>
</template> Demo test
I removed initial JSDoc comments:
import { createLocalVue, mount } from '@vue/test-utils'; // <== shallowMount removed because not used
import * as All from 'quasar';
import { VueConstructor } from 'vue';
import QBtnDemo from './demo/QBtn-demo';
// import langIt from 'quasar/lang/it' // change to any language you wish! => this breaks wallaby :(
const { Quasar, date } = All;
function isComponent(value: any): value is VueConstructor {
return value && value.component && value.component.name != null;
}
const components = Object.keys(All).reduce<{[index: string]: VueConstructor}>((object, key) => {
const val = (All as any)[key];
if (isComponent(val)) {
object[key] = val;
}
return object;
}, {});
describe('Mount Quasar', () => {
const localVue = createLocalVue();
localVue.use(Quasar, { components });
// localVue.use(Quasar, { components, lang: langEn }); <= specify a language different from the default
const wrapper = mount(QBtnDemo, { localVue });
const vm = wrapper.vm;
it('stuff', () => {
// ... stuff
});
}); It think it's appropriated to also add a new file with more down to earth tests, because you probably want to show the component into more isolated tests (aka, where you load only the bare minimum Quasar components to make it work) and show the usage of shallowMount.
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { Quasar, QBtn } from 'quasar'; // <= cherry pick only the components you actually use
import { VueConstructor } from 'vue';
import QBtnDemo from './demo/QBtn-demo';
const localVue = createLocalVue();
localVue.use(Quasar, { components: { QBtn } }); // <= you should register every component you use. If not declared here, `shallowMount` won't be able to stub them
const factory = (propsData: any = {}) => {
return shallowMount(QBtnDemo, { // <= used `shallowMount` instead of `mount`, will stub all **registered** components into the template
localVue,
propsData,
});
};
describe('QBtnDemo', () => {
test('is a Vue instance', () => {
const wrapper = factory(); // <= when no props are not needed
// const wrapper = factory({ propName: propValue }); <= when props are needed
expect(wrapper.isVueInstance()).toBeTruthy();
});
}); If you are like me, you're probably asking yourself "is this black magic? How can that TS work for mounting? It doesn't know anything about the template and style data!" TODOTranspile libraries exported with ES6 syntax using
|
@IlCallo one thing that we did when adding support for typescript in the main cli was to not include the extension when reference the files for import. If you use node module loading strategy it should import the file without a need for the extension to be specified, so |
Seems like removing the extension will work for JS-version, but will break when using a TS file. I'll look better into it after I fix linting stuff |
Additional updates after unbound-method false positive it('has a created hook', () => {
// False positive
// See https://github.com/typescript-eslint/typescript-eslint/issues/692
// eslint-disable-next-line @typescript-eslint/unbound-method
expect(typeof vm.increment).toBe('function');
}); Use native startWith instead of RegExp const resizeObserverLoopError = 'ResizeObserver loop limit exceeded';
Cypress.on('uncaught:exception', err => {
if (err.message.startsWith(resizeObserverLoopError)) {
// returning false here prevents Cypress from
// failing the test
return false;
}
}); Use Avoid |
I'm using quasar/typescript and followed this guide to make the project work. Just a note here about importing ES6 modules in case it helps someone, and if someone can also help me understand what I did 😆 On my code I needed to import this quasar utils code: import { testPattern } from 'quasar/src/utils/patterns'; and export const testPattern = {
^^^^^^
SyntaxError: Unexpected token 'export' Strictly using the examples provided here wasn't helping me, so I updated I'm using const esModules = ['quasar/lang', 'quasar/src/utils'].join('|');
module.exports = {
globals: {
__DEV__: true
},
...
transform: {
'^.+\\.(js|html)$': 'babel-jest',
'^.+\\.tsx?$': 'ts-jest',
'.*\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub'
// use these if NPM is being flaky
// '.*\\.vue$': '<rootDir>/node_modules/@quasar/quasar-app-extension-testing-unit-jest/node_modules/vue-jest',
// '.*\\.js$': '<rootDir>/node_modules/@quasar/quasar-app-extension-testing-unit-jest/node_modules/babel-jest'
},
transformIgnorePatterns: [`<rootDir>/node_modules/(?!(${esModules}))`],
...
}; Now everything is working 👍 |
We will have to use dynamic dependencies here - which I don't like because that makes them "invisible" to real dep auditing.
Proposal: helper packages for extensions
The text was updated successfully, but these errors were encountered: