I am in the process of migrating an Angular application from v6 to v7. All is well except any test that compares enums. When I run my tests, I get many errors regarding my enums like so
ERROR in src/.../some-thing.component.spec.ts: error TS2345: Argument of type 'PlanDuration.SixMonths' is not assignable to parameter of type 'Expected<PlanDuration.TwelveMonths>'.
An example of test being run looks like this:
export enum PlanDuration {
SixMonths,
TwelveMonths
}
...
it('should toggle plan duration to six months if the event source id is the toggle duration and the event is not checked', () => {
component.selectedPlanDuration = PlanDuration.TwelveMonths;
component.handleToggle(event);
expect(component.selectedPlanDuration).toBe(PlanDuration.SixMonths); // Tests cannot run because of errors here
});
However, if I cast my enum to number, my tests work perfectly! This would be less than ideal to update my specs everywhere like this:
expect(component.selectedPlanDuration).toBe(<number> PlanDuration.SixMonths);
I'm unsure if I missed something in my package.json
. I've compared a fresh angular 7 project to my own projects and the versions of angular core, typescript, jasmine and karma between them are the same.
How can I get my tests to compare enums properly? Below is my package.json
"dependencies": {
"@angular/animations": "~7.2.0",
"@angular/common": "~7.2.0",
"@angular/compiler": "~7.2.0",
"@angular/core": "~7.2.0",
"@angular/forms": "~7.2.0",
"@angular/http": "~7.2.0",
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
"core-js": "^2.5.4",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.8.26",
"@angular/cdk": "^7.0.3",
"@angular/flex-layout": "7.0.0-beta.24",
"@angular/material": "7.3.6",
"hammerjs": "2.0.8",
"intl": "1.2.5",
"jshashes": "1.0.7",
"lodash-es": "4.17.11",
"request-promise-native": "1.0.5",
"stream": "0.0.2",
"timers": "0.1.1",
"url-search-params-polyfill": "5.0.0",
"xml2js": "0.4.19"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.13.0",
"@angular/cli": "~7.3.7",
"@angular/compiler-cli": "~7.2.0",
"@angular/language-service": "~7.2.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.2",
"@types/lodash-es": "4.17.1",
"gulp": "3.9.1",
"gulp-stylelint": "7.0.0",
"jasmine-data-provider": "2.2.0",
"karma-cli": "1.0.1",
"karma-junit-reporter": "1.2.0",
"karma-parallel": "0.3.0",
"karma-spec-reporter": "0.0.32",
"lodash": "4.17.11",
"moment": "2.22.2",
"npm": "6.0.0",
"protractor-beautiful-reporter": "1.2.5",
"protractor-jasmine2-screenshot-reporter": "0.5.0",
"stylelint": "9.6.0",
"stylelint-order": "1.0.0",
"tslint-jasmine-noSkipOrFocus": "1.0.9"
}
tsconfig.json:
{
"compileOnSave": false,
"compilerOptions": {
"importHelpers": true,
"preserveConstEnums": true,
"outDir": "./dist/out-tsc",
"baseUrl": "src",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noUnusedLocals": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2016",
"dom"
]
}
}
tsconfig.spec.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"target": "es5",
"baseUrl": "",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
I ran into issues a little while back where Angular was telling me that it could not access MyEnumValue of undefined
. After some fiddling, I found that exporting all enums as const
, and adding "preserveConstEnums": true
to my tsconfig.json made it work just fine.
But enums are always numbers unless otherwise specified, and thankfully don't need casting, but Typescript's compilation of enums can be funky at times in the same way interfaces are funky.
Edit:
In your component, make sure:
// If you give this a default value, TypeScript will assume
// that the only "valid" type is PlanDuration.TwelveMonths
// Type evaluates to: PlanDuration | number between 0 and 1;
selectedPlanDuration: PlanDuration = PlanDuration.TwelveMonths;
// Type evaluates to: PlanDuration.TwelveMonths | 1;
selectedPlanDuration = PlanDuration.TwelveMonths
TL;DR
Quick fix: go to node_modules/@types/jasmine/index.d.ts
, search for type Expected
// Change this line:
// type Expected<T> = T | ObjectContaining<T> | Any | Spy;
// to:
type Expected<T> = any;
That's it :)
For more details, read on:
I believe this is a bug in jasmine's types definition. I set up a fresh ng7 workspace and try to reproduce your problem. Here's my finding:
There are two jasmine related .d.ts
file in the workspace:
// package.json
...
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
I'm not 100% sure how these two work together, but they are declaring conflicting types for same jasmine utils. For example:
// jasminewd2/index.d.ts
declare namespace jasmine {
interface Matchers<T> {
toBe(expected: any, expectationFailOutput?: any): Promise<void>;
...
// jasmine/index.d.ts
declare namespace jasmine {
interface Matchers<T> {
toBe(expected: Expected<T>, expectationFailOutput?: any): boolean;
Now the problem is in jasmine/index.d.ts
.
This line toBe(expected: Expected<T>)
is simply WRONG. This is a test case, certainly you are allowed to test against any
value. Yet Expected<T>
is declared as some complex type of no point.
The easiest way to fix it, is to manually correct it. Solution is already given at the beginning. Cheers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With