Не удается сравнить перечисления в jasmine после угловой миграции с 6 на 7

#angular #enums #angular6 #karma-jasmine #angular7

#angular #перечисления #angular6 #karma-jasmine #angular7

Вопрос:

Я нахожусь в процессе переноса приложения Angular с версии 6 на версию 7. Все хорошо, за исключением любого теста, который сравнивает перечисления. Когда я запускаю свои тесты, я получаю много ошибок, касающихся моих перечислений, вот так

 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>'.
  

Пример выполняемого теста выглядит следующим образом:

 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
    });
  

Однако, если я приведу свое перечисление к number, мои тесты будут работать отлично! Это было бы не идеально для обновления моих спецификаций везде, как это:

 expect(component.selectedPlanDuration).toBe(<number> PlanDuration.SixMonths);
  

Я не уверен, что я что-то пропустил в своем package.json . Я сравнил новый проект angular 7 со своими собственными проектами, и версии angular core, typescript, jasmine и karma между ними одинаковы.

Как я могу заставить свои тесты правильно сравнивать перечисления? Ниже мой 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"
  ]
}
  

Комментарии:

1. Попробуйте вместо этого «экспортировать постоянное перечисление …» и в вашем TSConfig попробуйте установить preserveConstEnums: true . Перечисления на самом деле являются числами, если не указано иное, и не нуждаются в приведении, но Typescript отлично компилирует с перечислениями, которые я нашел

2. @JonathanSchmold, ты можешь опубликовать это в качестве ответа? Я могу попробовать ваше предложение в понедельник утром, и если это сработает, я с радостью присужду вам эту награду.

3. Я, конечно, могу 🙂

4. Я предполагаю, что это может быть проблемой с Karma и Jasmine. Я видел некоторые, и мне пришлось отказаться от 4-й версии до 3.99

5. Это ошибка в определении типа jasmine. Проверьте мой ответ на детали и как исправить.

Ответ №1:

Некоторое время назад я столкнулся с проблемами, когда Angular сообщал мне, что он не может получить доступ MyEnumValue of undefined . После некоторой проверки я обнаружил, что экспорт всех перечислений как const и добавление "preserveConstEnums": true в мой tsconfig.json заставили его работать просто отлично.

Но перечисления всегда являются числами, если не указано иное, и, к счастью, не нуждаются в приведении, но компиляция перечислений в Typescript иногда может быть обалденной, точно так же, как обалденные интерфейсы.

Редактировать:

В вашем компоненте убедитесь, что:

 // 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
  

Комментарии:

1. Я попробовал ваше предложение, а также сопоставил tsconfig между ванильным приложением ng7 и нашим рабочим приложением, и не смог заставить это работать. Я чувствую, что это проблема с TS, хотя и продолжу расследование.

2. попробуйте expect(component.selectedPlanDuration.valueOf())

3. Эй, на самом деле, аннотацию какого типа вы дали компоненту? Я чувствую, что вы определили это как что-то необычное, и что тип, назначенный selectedPlanDuration буквально PlanDuration.TwelveMonths . Typescript позволяет присваивать «тип» переменной как единственное значение одного типа. let q: 3 = 4; // error

4. Я обновил свой ответ, чтобы охватить то, что могло быть проблемой, но не было указано в вашем вопросе. Вероятно, это проблема

5. В моем компоненте есть этот элемент: public selectedPlanDuration: PlanDuration = PlanDuration.SixMonths; . Предложение valueOf работает, хотя, однако, не идеально для исправления многих спецификаций, содержащих этот метод.

Ответ №2:

TL; DR

Быстрое исправление: перейдите к node_modules/@types/jasmine/index.d.ts , найдите type Expected

 // Change this line:
// type Expected<T> = T | ObjectContaining<T> | Any | Spy;
// to:
type Expected<T> = any;
  

Вот и все 🙂


Для получения более подробной информации читайте дальше:

Я считаю, что это ошибка в определении типов jasmine. Я настраиваю новое рабочее пространство ng7 и пытаюсь воспроизвести вашу проблему. Вот мой вывод:

В рабочей области есть два связанных с jasmine .d.ts файла:

 // package.json
  ...
  "@types/jasmine": "~2.8.8",
  "@types/jasminewd2": "~2.0.3",
  

Я не уверен на 100%, как эти два работают вместе, но они объявляют конфликтующие типы для одних и тех же утилит jasmine. Например:

 // 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;
  

Теперь проблема в jasmine/index.d.ts .

Эта строка toBe(expected: Expected<T>) просто НЕВЕРНА. Это тестовый пример, конечно, вам разрешено протестировать any значение. Все же Expected<T> объявлено как некоторый сложный тип без точки.

Самый простой способ исправить это — исправить вручную. Решение уже приведено в начале. Приветствия.

Комментарии:

1. Дополнительное замечание. Я лично считаю, что проверка ts исходного файла тестового примера во время компиляции является излишеством и должна быть отключена навсегда. Проверка Ts во время отключения все в порядке.

2. Это быстрое исправление имеет смысл, но как это вообще будет работать при настройке сервера производственной сборки? Возможно, лучшим вариантом (кроме отправки PR ) является переопределение этого определения типа и использование его в тестах. Хотя это все равно потребовало бы изменения в каждом файле, который сравнивает перечисления, а OP не хочет этого делать.

3. Должно помочь объединение объявлений @Nanotron. У меня нет компьютера рядом, поэтому я не могу его протестировать, но я думаю, это должно сработать: добавьте patchJasmine.d.ts куда-нибудь, что включено в tsconfig.json , затем в файл вы добавляете это: declare namespace jasmine { type Expected<T> = any; } .

Ответ №3:

Я столкнулся с той же проблемой при реализации новых тестов в проекте Angular 7 (без переноса с 6 на 7). Если вы предпочитаете обходной путь, а не внесение изменений, которые могут повлиять на будущие обновления Karma / Jasmine, вы можете поменять сравнения местами, чтобы перечисление было первым:

 expect(PlanDuration.SixMonths).toBe(component.selectedPlanDuration);
  

Комментарии:

1. Я не уверен, почему за это проголосовали, но я попал сюда, выполнив поиск, почему перечисления в моих тестах, где возникали ошибки, и этот метод был наиболее вероятным для меня (и я бы предположил, что большинство людей, которые сталкиваются с этим сообщением) для реализации.