0

In an Angular and Jest project, we are looking to improve the response times of unit tests. Looking for solutions, everything pointed to using SWC within the project, we have already implemented it, but we received an error in the components when we passed the tests.

package.json

{
  "name": "app-ui",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "postinstall": "ngcc && husky install",
    "start": "ng serve --proxy-config proxy.conf.json",
    "start:dev": "ng serve -o --live-reload --configuration development --proxy-config proxy.conf.json",
    "start:local": "ng serve -o --live-reload --configuration development --proxy-config proxy.conf.local.json",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "jest --detect-open-handles",
    "test1": "cross-env NODE_ENV=test jest --config ./jest.config.mjs",
    "test2": "node --experimental-vm-modules --no-warnings ./node_modules/jest/bin/jest.js --config ./jest.config.mjs --ci --coverage",
    "test:browser": "node --experimental-vm-modules --no-warnings ./node_modules/jest/bin/jest.js --config ./jest.config.mjs",
    "test:watch": "jest --watch",
    "test:coverage": "jest --ci --coverage",
    "test:cc": "jest --ci --coverage --coverageReporters='text-summary'",
    "test:pre": "jest --ci --coverage --coverageReporters='text-summary' --silent --detectOpenHandles",
    "test:deploy": "jest --ci --coverage --detect-open-handles --forceExit",
    "test:report": "allure serve allure-results/",
    "eslint": "eslint -c .eslintrc.json --ext .ts src/ --ignore-pattern node_modules/ --fix",
    "lint-report": "eslint -c .eslintrc.json --ext .ts src/ --ignore-pattern node_modules/ --fix -f node_modules/eslint-html-reporter/reporter.js -o report.html",
    "format": "prettier --write \"src/**/*.{js,ts,css,json,html,scss}\" --config ./.prettierrc.json",
    "sonar": "sonar-scanner",
    "unused-css": "ngx-unused-css"
  },
  "private": true,
  "type": "module",
  "dependencies": {
    "@angular/animations": "^15.1.1",
    "@angular/cdk": "^15.1.1",
    "@angular/common": "^15.1.1",
    "@angular/compiler": "^15.1.1",
    "@angular/core": "^15.1.1",
    "@angular/forms": "^15.1.1",
    "@angular/material": "^15.1.1",
    "@angular/material-moment-adapter": "^15.1.1",
    "@angular/platform-browser": "^15.1.1",
    "@angular/platform-browser-dynamic": "^15.1.1",
    "@angular/router": "^15.1.1",
    "@azure/storage-blob": "^12.12.0",
    "@ngx-translate/core": "^14.0.0",
    "@ngx-translate/http-loader": "^7.0.0",
    "@popperjs/core": "^2.11.6",
    "@types/event-source-polyfill": "^1.0.1",
    "angular-auth-oidc-client": "15.0.3",
    "apexcharts": "^3.36.3",
    "bootstrap": "^5.2.3",
    "classlist.js": "^1.1.20150312",
    "esbuild-wasm": "0.17.4",
    "event-source-polyfill": "^1.0.31",
    "moment": "^2.29.4",
    "ng-apexcharts": "^1.7.4",
    "ngx-material-timepicker": "^5.5.3",
    "rxjs": "~7.8.0",
    "tslib": "^2.4.1",
    "web-animations-js": "^2.3.2",
    "zone.js": "~0.12.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^15.1.2",
    "@angular-eslint/builder": "15.2.0",
    "@angular-eslint/eslint-plugin": "15.2.0",
    "@angular-eslint/eslint-plugin-template": "15.2.0",
    "@angular-eslint/schematics": "15.2.0",
    "@angular-eslint/template-parser": "15.2.0",
    "@angular/cli": "^15.1.2",
    "@angular/compiler-cli": "^15.1.1",
    "@babel/core": "^7.20.12",
    "@babel/plugin-transform-typescript": "^7.20.13",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-typescript": "^7.18.6",
    "@briebug/jest-schematic": "^6.0.0",
    "@jest/globals": "^29.4.0",
    "@ngneat/spectator": "^14.0.0",
    "@swc/cli": "^0.1.59",
    "@swc/core": "^1.3.28",
    "@swc/jest": "^0.2.24",
    "@types/jest": "^29.4.0",
    "@types/node": "^18.11.18",
    "@typescript-eslint/eslint-plugin": "5.49.0",
    "@typescript-eslint/eslint-plugin-tslint": "^5.49.0",
    "@typescript-eslint/parser": "5.49.0",
    "@typescript-eslint/typescript-estree": "^5.49.0",
    "allure-commandline": "^2.20.1",
    "babel-core": "^6.26.3",
    "babel-jest": "^29.4.0",
    "babel-preset-jest": "^29.4.0",
    "cross-env": "^7.0.3",
    "cypress": "^12.4.0",
    "esbuild": "^0.17.4",
    "esbuild-jest": "^0.5.0",
    "eslint": "^8.32.0",
    "eslint-config-prettier": "^8.6.0",
    "eslint-html-reporter": "^0.7.4",
    "eslint-plugin-angular": "^4.1.0",
    "eslint-plugin-import": "^2.27.5",
    "eslint-plugin-jsdoc": "^39.6.8",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-sonarjs": "^0.18.0",
    "husky": "^8.0.3",
    "jest": "^29.4.0",
    "jest-allure": "^0.1.3",
    "jest-allure-image-snapshot": "^0.0.10",
    "jest-browser": "^0.1.0",
    "jest-css-modules-transform": "^4.4.2",
    "jest-environment-jsdom": "^29.4.0",
    "jest-jasmine2": "^29.4.0",
    "jest-junit-reporter": "^1.1.0",
    "jest-preset-angular": "^12.2.5",
    "jest-raw-loader": "^1.0.1",
    "lint-staged": "^13.1.0",
    "ngx-unused-css": "^4.0.0-1",
    "prettier": "^2.8.3",
    "prettier-eslint": "^15.0.1",
    "sonar-scanner": "^3.1.0",
    "ts-jest": "^29.0.5",
    "ts-node": "^10.9.1",
    "tslint": "^6.1.3",
    "tslint-angular": "^3.0.3",
    "typescript": "~4.8.4"
  },
  "prettier": {
    "printWidth": 150,
    "tabWidth": 2,
    "useTabs": false,
    "semi": true,
    "singleQuote": true,
    "trailingComma": "none",
    "bracketSpacing": true,
    "arrowParens": "always",
    "proseWrap": "preserve",
    "htmlWhitespaceSensitivity": "css",
    "endOfLine": "auto",
    "quoteProps": "consistent",
    "overrides": [
      {
        "files": [
          "**/*.css",
          "**/*.scss",
          "**/*.html"
        ],
        "options": {
          "singleQuote": false
        }
      }
    ]
  },
  "lint-staged": {
    "src/**/*.{js,ts,scss,md,html,json}": [
      "prettier --write",
      "git add"
    ],
    "src/**/*.{js,ts}": [
      "npm run eslint",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "pre-push": "npm run eslint && ng build --aot true"
    }
  }
}

.swcrc

{
  "test": ".*.ts$",
  "jsc": {
    "target": "es2022",
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": false,
      "dynamicImport": false
    },
    "externalHelpers": true
  },
  "module": {
    "type": "commonjs"
  },
  "sourceMaps": true
}

tsconfig.spec.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": ["jest","node"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "module": "CommonJs",
    "moduleResolution": "node",
    "target": "ES2022",
    "sourceMap": true,
    "inlineSources": true,
    "inlineSourceMap": true,
    "paths": {
      "@shared/*": ["./src/app/shared/*"],
      "@services/*": ["./src/app/services/*"],
      "@utils/*": ["./src/app/utils/*"],
      "@pages/*": ["./src/app/pages/*"],
      "@global/*": ["./src/app/global/*"],
      "@guards/*": ["./src/app/guards/*"],
      "@auth/*": ["./src/app/auth/*"],
      "@mock/*": ["./src/mock/*"],
      "@helpers/*": ["./src/helpers/*"],
      "@pipes/*": ["./src/app/pipes/*"],
      "@directives/*": ["./src/app/directives/*"],
      "@constants/*": ["./src/app/constants/*"],
      "@interceptors/*": ["./src/app/interceptors/*"]
    }
  },
  "include": ["src/**/*.spec.ts","src/**/*.d.ts"]
}

jest.config.mjs

const esModules = ['@angular'].join('|');

export default {
  collectCoverage: true,
  testRunner: 'jest-jasmine2',
  coverageReporters: ['json', 'lcov', 'html'],
  collectCoverageFrom: [
    'src/**/*.{js,ts}',
    '!src/**/*.module.{js,ts}',
    '!src/**/*-routing.module.{js,ts}',
    '!src/**/index.{js,ts}',
    '!src/**/main.{js,ts}',
    '!src/**/polyfills.{js,ts}',
    '!src/**/vendor.{js,ts}'
  ],
  extensionsToTreatAsEsm: ['.ts', '.tsx'],
  testEnvironment: 'jsdom',
  globalSetup: 'jest-preset-angular/global-setup.mjs',
  timers: 'fake',
  setupFilesAfterEnv: ['<rootDir>/setup-jest.mjs', 'jest-allure/dist/setup'],
  globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tsconfig.spec.json',
      isolatedModules: true,
      stringifyContentPathRegex: '\\.(html|svg)$',
      allowSyntheticDefaultImports: true,
      useESM: true
    }
  },
  roots: ['<rootDir>/src'],
  transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
  moduleNameMapper: {
    'tslib': 'tslib/tslib.es6.js',
    '^rxjs$': '<rootDir>/node_modules/rxjs/dist/bundles/rxjs.umd.js',
    '^rxjs/operators$': '<rootDir>/node_modules/rxjs/dist/bundles/rxjs.umd.js',
    '^rxjs/testing$': '<rootDir>/node_modules/rxjs/dist/cjs/testing/index.js',
    '^rxjs/webSocket$': '<rootDir>/node_modules/rxjs/dist/cjs/webSocket/index.js',
    '^rxjs/ajax$': '<rootDir>/node_modules/rxjs/dist/cjs/ajax/index.js',
    '@angular/core/testing': '<rootDir>/node_modules/@angular/core/fesm2020/testing.mjs',
    '@angular/platform-browser/testing': '<rootDir>/node_modules/@angular/platform-browser/fesm2020/testing.mjs',
    '@angular/platform-browser-dynamic/testing': '<rootDir>/node_modules/@angular/platform-browser-dynamic/fesm2020/testing.mjs',
    '^app/(.*)$': '<rootDir>/src/app/$1',
    '^assets/(.*)$': '<rootDir>/src/assets/$1',
    '^environments/(.*)$': '<rootDir>/src/environments/$1',
    '^@shared/(.*)$': '<rootDir>/src/app/shared/$1',
    '^@constants/(.*)$': '<rootDir>/src/app/constants/$1',
    '^@services/(.*)$': '<rootDir>/src/app/services/$1',
    '^@utils/(.*)$': '<rootDir>/src/app/utils/$1',
    '^@pages/(.*)$': '<rootDir>/src/app/pages/$1',
    '^@global/(.*)$': '<rootDir>/src/app/global/$1',
    '^@guards/(.*)$': '<rootDir>/src/app/guards/$1',
    '^@mock/(.*)$': '<rootDir>/src/mock/$1',
    '^@helpers/(.*)$': '<rootDir>/src/helpers/$1',
    '^@pipes/(.*)$': '<rootDir>/src/app/pipes/$1',
    '^@directives/(.*)$': '<rootDir>/src/app/directives/$1',
    '^@auth/(.*)$': '<rootDir>/src/app/auth/$1',
    '^@interceptors/(.*)$': '<rootDir>/src/app/interceptors/$1'
  },
  transform: {
    '^.+\\.[tj]s?$': [
      '@swc/jest',
      {
        jsc: {
          target: 'es2022'
        },
        sourceMaps: true
      }
    ]
  },
  maxWorkers: '8'
};

Resultado:

Test Suites: 206 failed, 1 passed, 207 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        31.545 s
Ran all test suites.

Resultado de app.component

 FAIL  src/app/app.component.spec.ts
  ● Test suite failed to run


      x Expression expected
        ,-[C:\Users\xxxxxxxx\src\app\app.component.ts:6:1]
      6 | import { Subscription } from 'rxjs';
      7 | import { SidebarService } from '@services/sidebar/sidebar.service';
      8 |
      9 | @Component({
        : ^
     10 |   selector: 'app-root',
     11 |   templateUrl: './app.component.html',
     11 |   styleUrls: ['./app.component.scss'],
        `----


    Caused by:
        Syntax Error

          ,-[C:\Users\xxxxxxxx\src\app\app.component.ts:6:1]
        6 | import { Subscription } from 'rxjs';
        7 | import { SidebarService } from '@services/sidebar/sidebar.service';
        8 |
        9 | @Component({
          : ^
       10 |   selector: 'app-root',
       11 |   templateUrl: './app.component.html',
       11 |   styleUrls: ['./app.component.scss'],
          `----
      Caused by:
          Syntax Error

otros resultados

 FAIL  src/app/pipes/numbercomparatorpipe.pipe.spec.ts
  ● Test suite failed to run


      x Expression expected
       ,-[C:\Users\XXXXXXXX\src\app\pipes\numbercomparatorpipe.pipe.ts:1:1]
     1 | import { Pipe, PipeTransform } from '@angular/core';
     2 |
     3 | @Pipe({
       : ^
     4 |   name: 'comparatorpipe'
     5 | })
     5 | export class NumberComparatorPipe implements PipeTransform {
       `----


    Caused by:
        Syntax Error

      ,-[C:\Users\XXXXXXXX\src\app\pipes\numbercomparatorpipe.pipe.ts:1:1]
       1 | import { Pipe, PipeTransform } from '@angular/core';
       2 |
       3 | @Pipe({
         : ^
       4 |   name: 'comparatorpipe'
       5 | })
       5 | export class NumberComparatorPipe implements PipeTransform {
         `----
      Caused by:
          Syntax Error

 FAIL  src/app/interceptors/login/NotEmployees.interceptor.spec.ts
  ● Test suite failed to run


      x Expression expected
        ,-[C:\Users\XXXXXXXX\src\app\interceptors\login\NotEmployees.interceptor.ts:6:1]
      6 | import { TranslateService } from '@ngx-translate/core';
      7 | import { OidcSecurityService } from 'angular-auth-oidc-client';
      8 | import { UserApiService } from '@services/api/user-api/user-api.service';
      9 | @Injectable()
        : ^
     10 | export class NotEmployees implements HttpInterceptor {
     11 |   size: string;
     11 |
        `----


    Caused by:
        Syntax Error

          ,-[C:\Users\XXXXXXXX\src\app\interceptors\login\NotEmployees.interceptor.ts:6:1]
        6 | import { TranslateService } from '@ngx-translate/core';
        7 | import { OidcSecurityService } from 'angular-auth-oidc-client';
        8 | import { UserApiService } from '@services/api/user-api/user-api.service';
        9 | @Injectable()
          : ^
       10 | export class NotEmployees implements HttpInterceptor {
       11 |   size: string;
       11 |
          `----
      Caused by:
          Syntax Error

 FAIL  src/app/pipes/form-control-pipe.pipe.spec.ts
  ● Test suite failed to run


      x Expression expected
       ,-[C:\Users\XXXXXXXX\src\app\pipes\form-control-pipe.pipe.ts:1:1]
     1 | import { Pipe, PipeTransform } from '@angular/core';
     2 | import { AbstractControl, UntypedFormControl } from '@angular/forms';
     3 |
     4 | @Pipe({
       : ^
     5 |   name: 'formControlPipe'
     6 | })
     6 | export class FormControlPipe implements PipeTransform {
       `----


    Caused by:
        Syntax Error

         ,-[C:\Users\XXXXXXXX\src\app\pipes\form-control-pipe.pipe.ts:1:1]
       1 | import { Pipe, PipeTransform } from '@angular/core';
       2 | import { AbstractControl, UntypedFormControl } from '@angular/forms';
       3 |
       4 | @Pipe({
         : ^
       5 |   name: 'formControlPipe'
       6 | })
       6 | export class FormControlPipe implements PipeTransform {
         `----
      Caused by:
          Syntax Error

I already tried implementing babel vs esbuild and both give me similar errors, so I do not think it is the library but something in the jest or angular configuration.

  • You are going to be out of luck until SWC supports Angular. It is quite close but not yet available: https://github.com/swc-project/swc-node/issues/615 – Flavian Hautbois Feb 01 '23 at 14:21

0 Answers0