nstudio / xplat

Cross-platform (xplat) tools for Nx workspaces.
MIT License
367 stars 52 forks source link

Jest and Karma conflict issue #170

Closed dmitryr117 closed 4 years ago

dmitryr117 commented 4 years ago

When making after creating a new Angular app in the workspace and trying to run a test on a fresh install using:

npm run affected:test

All tests fail. There seems to be an issue in node_modules/jest-preset-angular/setupJest.js because: Cannot find module 'core-js/es6/reflect' from 'setupJest.js' This issue is easily fixed by editing setupJest.js file, removing lines containing: core-js/es6/reflect and core-js/es7/reflect , and replacing them with core-js/es/reflect.

However there are other tests for /libs and /xplat folders, and those are configured for Karma. When running tests - I get:

Cannot find module 'karma' Require stack: ...

How do I go about fixing it to make the whole project use Jest, or Karma if it is easier for now. Thanks.

dmitryr117 commented 4 years ago

Sort of figured out how to make Jest work with /libs and /xplat and documented it for future reference. Here it is:

// Jest testing setup: In order to allow for Jest testing - it is necessary to edit a file setupJest.js in a node_packages folder and replace

core-js/es6/reflect and core-js/es6/reflect path areas with just core-js/es/reflect. -- verify that folder structure has not changed.

Keep in mind that so far all tests are being kept in Karma format. This means that they need to be replaced with Jest format otherwise there will be errors on clean install testing.

In order to make Jest work with /libs and /xplat folder it is necessary to modify: /workspace.json, remove: /testing karma related files add: /testing/tsconfig.json, /testing/test-setup.ts /testing/jest.libs.config.js, /testing/jest.xplat.config.js, /testing/tsconfig.libs.spec.json, /testing/tsconfig.xplat.spec.json,

Here are the contents of each file:


tsconfig.json:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "types": ["node", "jest"]
  },
  "include": ["**/*.ts"]
}

test-setup.ts

import 'jest-preset-angular';

jest.libs.config.js

module.exports = {
  name: 'libs',
  preset: '../jest.config.js',
  rootDir: '../libs',
  coverageDirectory: '../coverage/libs',
  snapshotSerializers: [
    'jest-preset-angular/AngularSnapshotSerializer.js',
    'jest-preset-angular/HTMLCommentSerializer.js'
  ]
};

jest.xplat.config.js

module.exports = {
  name: 'xplat',
  preset: '../jest.config.js',
  rootDir: '../xplat',
  coverageDirectory: '../coverage/xplat',
  snapshotSerializers: [
    'jest-preset-angular/AngularSnapshotSerializer.js',
    'jest-preset-angular/HTMLCommentSerializer.js'
  ]
};

tsconfig.libs.spec.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../dist/out-tsc/libs",
    "types": ["jest", "node"]
  },
  "files": ["test-setup.ts"],
  "include": [
    "../libs/**/*.spec.ts",
    "../libs/**/*.d.ts"
  ]
}

tsconfig.xplat.spec.json

{
  "extends": "./tsconfig.libs.spec.json",
  "compilerOptions": {
    "outDir": "../dist/out-tsc/xplat",
  },
  "files": ["test-setup.ts"],
  "include": [
    "../libs/**/*.spec.ts",
    "../libs/**/*.d.ts",
    "../xplat/web/**/*.spec.ts",
    "../xplat/web/**/*.d.ts"
  ]
}

After this the /workspace.json has to be modified like so:

...
  "projects": {
    "libs": {
      ...
      "architect": {
        "test": {
          "builder": "@nrwl/jest:jest",
          "options": {
            "jestConfig": "testing/jest.libs.config.js",
            "tsConfig": "testing/tsconfig.libs.spec.json",
            "setupFile": "testing/test-setup.ts"
          }
        },
        "lint": {
            ...
        }
      }
    },
    "xplat": {
      ...
      "architect": {
        "test": {
          "builder": "@nrwl/jest:jest",
          "options": {
            "jestConfig": "testing/jest.xplat.config.js",
            "tsConfig": "testing/tsconfig.xplat.spec.json",
            "setupFile": "testing/test-setup.ts"
          }
        },
        "lint": {
          ...
        }
      }
    },
 ...

Ellipsis represents places that should be left alone. Most important areas are the areas where we define testing.

So far I tested this with basic test behaviours, and it seems to work, but all default tests fail. Probably because they are all written for Karma by default, but I didn't investigate this yet.

This is a hacky solution that I managed to come up with in a few hours and I am by no means a Jest configuration expert. There probably is a cleaner solution, but this seems to work for now.

draylegend commented 4 years ago

@dmitryr117 do you mean to edit a file in the node_modules folder? If so this is a bad idea, cause the next package update will reset all files.

dmitryr117 commented 4 years ago

For now yeah. But I am pretty sure that this is a bug in the package. At some point it will have to be fixed because clearly the path in the package is wrong.

dmitryr117 commented 4 years ago

I read around a bit, and it seems that NX was reconfigured to use Jest by default, however when choosing to use Xplat architecture - Xplat automatically generates karma configuration for /libs and /xplat directories. I hunted around the Xplat source a bit and it looks like it is coded inside /packages/angular/src/utils/xplat.ts. There must be a way to dynamically generate a Karma or Jest config, however I just recently started learning schematics development, so it will take me some time to provide a possible solution.

draylegend commented 4 years ago

@dmitryr117 I have faced same behaviour of xplat. I think, it would be great if xplat could generate only jest or karma config but not both of them. After creating a new xplat workspace, you can't directly test. You need first adjust jest tests to get it running.

After generating a new app, xplat schematics generates the testing folder. I've tried to remove it but it appears after every new app was added. I think, it would be better to move lib and xplat specific tsconfig files into libs and xplat folders respectively. For libs: libs/tsconfig.json, libs/tsconfig.spec.json, jest.config.js For xplat: xplat/tsconfig.json, xplat/tsconfig.spec.json, jest.config.js And remove the testing folder. My repo.

We need to consider the nativescript {N} unit testing. It doesn't have jest support right now. And I don't know, how to switch to jest. It would be great if the {N} had the opportunity to test using the jest.

I also noticed that karma related stuff is alway installed, no matter what you selected previously. If we wanna go cross platform, I think we need to consider what mobile framework we wanna choose. Ionic seems to be working with jest but not {N}. There is a lot of stuff to consider..

dmitryr117 commented 4 years ago

@vladimirdrayling Creation of Karma configuration is coded into xplat schematic. packages/angular/src/utils/xplat.ts on line 278 it checks for existense of Karma configuration:

if (tree.exists(`testing/karma.conf.js`)) {
  return noop();
}

So this is the culprit file where creation of Karma config is hard coded and it creates karma configuration every time angular project is created and testing/karma.conf.js file is missing.

NS Jest testing

Also regarding NativeScript testing with Jest. There is a way to do it apparently but only if you do not use the tns and configure it to use regular ng test instead. After all NS TypeScript code is for the most part Angular code. Here is a chunk of a blog post I am in the process of writing for how to do this stuff with NS. See if it helps.

NativeScript

Create NativeScript Application

Now that we have our workspace - it is time to generate our NativeScrict Application

Generating a project

Generate a project using one of these two methods: nx generate app or npx nx generate app Use npx generation method if you do not want to install @nrwl/cli globally. In this case however you will need to prefix your nx commands with npx every time to let npm know that you want to use local package.

Follow prompts as follows:

Ok Done! If you go into apps folder - you will see your project folder sitting there in a folder named something like: nativescript-nsproject or nativescript-[name-you-chose].

Nativescript Fixing Test Configuration

I will be using ~ws/ notation to denote workspace root folder so that it is easy for the reader to easily follow paths I describe.

Missing Files

There are a few missing files which we will need to create in order to be able to do unit testing of NativeScript fith Angular using Jest. In workspace root: We need to add jest.config.js to the root of our workspace.

Keep in mind that ~/ws/jest.config.js will already be present in the root if you previously set up a web project with Angular. So make sure to check if it is there already.

Create/Update **~ws/jest.config.js

module.exports = {
  testMatch: [
    '**/+(*.)+(spec|test).+(ts|js)?(x)'
  ],
  transform: {
    '^.+\\.(ts|js|html)$': 'ts-jest'
  },
  resolver: '@nrwl/jest/plugins/resolver',
  moduleFileExtensions: ['ts', 'js', 'html'],
  coverageReporters: ['html'],
  passWithNoTests: true,
  verbose: true
};

Now we have our tester configuration which will tell Jest to use files ending with: *.spec.ts, *.spec.js, *.test.ts, and *.test.js,

Another set of missing files that we have to worry about should be is sitting inside ~ws/apps/[[nativescript_app_dir]/ directory. These are a local project version of jest.config.js and tsconfig.spec.js They should bave the following code in them:

~ws/apps/[[nativescript_app_dir]]/jest.config.js

module.exports = {
  name: 'nativescript-nstest',
  preset: '../../jest.config.js',
  coverageDirectory: '../../coverage/apps/nativescript-nstest',
  snapshotSerializers: [
    'jest-preset-angular/AngularSnapshotSerializer.js',
    'jest-preset-angular/HTMLCommentSerializer.js'
  ]
};

~ws/apps/[nativescript_app_dir]/tsconfig.spec.js

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "commonjs",
    "types": ["jest", "node"]
  },
  "files": ["src/test-setup.ts"],
  "include": ["**/*.spec.ts", "**/*.d.ts"]
}

There is one more file we need to add. It should be placed in the following folder: ~ws/apps/[nativescript_app_dir]/src/ And it should be called test-setup.ts Here are irs contents:

~ws/apps/[nativescript_app_dir]/src/test-setup.ts

import 'jest-preset-angular';

That's it. We will be linking all these files to our workspace in the next section.

Editing some JSON to enable testing.

The file we are most interested in here is sitting in project root: ~ws/workspace.json This is where our workspace is described. Now we have to tell it how to test our NativeScript app. Inside this file there is a projects dictionary which holds all the different projects that we have.

"projects": {
                  ...,
  architect: {
    "serve": {
       ...
    },
     "test": {
       [THIS IS EHERE THE MAGIC HAPPENS]
     }
  }
}

We have to add a test object as a child of the architect to define tests.

Make sure that you do not already have this defined for your project. As of now the workspace does not generate this clause inside the workspace.json

Now. Here is how this test have to be formatted:

...
"test": {
  "builder": "@nrwl/jest:jest",
  "options": {
      "jestConfig": "apps/[nativescript_app_dir]/jest.config.js",
      "tsConfig": "apps/[nativescript_app_dir]/tsconfig.spec.json",
       "setupFile": "apps/[nativescript_app_dir]/src/test-setup.ts"
  }
}

First me specify the test builder to be @nrwl/jest:jest. And in the options section we have all the files that we created before being linked to the workspace testing mechanism.

Editing of files is still not done however. If we try to run npm run affected:test - it will die screaming. We are missing some packages, and there is also a little bug in one of the packages as well. Not to worry. We will deal with it like … Now!

Fixing Dependency Issues

Personally I would recomment creating a Web Angular project, and adding just one package to your devDependencies inside of ~ws/package.json. It is "jest-jasmine2": "^24.1.0". This is just a package to use Jasmine syntax with Jest.

Otherwise if you don't want to install web project with angular - there are a lot more packages to add:

"jest-preset-angular": "7.0.0",
"@nrwl/jest": "8.8.3",
"jest-jasmine2": "^24.1.0",
"jest": "24.1.0",
"@types/jest": "24.0.9",
"ts-jest": "24.0.0",
"core-js": "^3.3.2",
"zone.js": "~0.10.2",
"@angular/core": "^8.2.0",
"@angular/common": "^8.2.0",
"@angular/compiler": "^8.2.0",
"@angular/platform-browser": "^8.2.0",
"@angular/platform-browser-dynamic": "^8.2.0"

You may have to update yopur versions as required. I put all of these in devDependencies inside ~ws/package.json and ran npm i.

Please make sure to inspect your package.json for these packages. If you created angular app in the worksopace prior to this - many of the above packages will already be installed inside regular dependencies dictionary.

Once packages are installed you can try running npm run affected:test. And unless this problem is fixed by the time you are reading this - it will die screaming again. And it will throw an error that will have something like this in it. Cannot find module 'core-js/es6/reflect' from 'setupJest.js' at Resolver.resolveModule (../../node_modules/jest-resolve/build/index.js:259:17) at Object.<anonymous> (../../node_modules/jest-preset-angular/setupJest.js:3:1) We have to fix setupJest.js file inside of the jest-preset-angular package file ourselves for now. Maybe in the future you will no longed have to do this if the package gets repaired. When we open ~ws/node_modules/jest-preset-angular/setupJest.js" as of now here are its contents:

'use strict';

require('core-js/es6/reflect');
require('core-js/es7/reflect');
require('zone.js/dist/zone.js');
require('zone.js/dist/proxy.js');
require('zone.js/dist/sync-test');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('./zone-patch');

const getTestBed = require('@angular/core/testing').getTestBed; 
const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;   
const platformBrowserDynamicTesting = require('@angular/platform-browser-dynamic/testing').platformBrowserDynamicTesting;   

getTestBed().initTestEnvironment(   
  BrowserDynamicTestingModule,  
  platformBrowserDynamicTesting()   
);

We have to change couple of lines on the top so that the file looks like this:

'use strict';

require('core-js/es/reflect');
require('zone.js/dist/zone.js');
require('zone.js/dist/proxy.js');
require('zone.js/dist/sync-test');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('./zone-patch');

const getTestBed = require('@angular/core/testing').getTestBed; 
const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule;
const platformBrowserDynamicTesting = require('@angular/platform-browser-dynamic/testing').platformBrowserDynamicTesting;

getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);

After this change - testing should start working ok. Finally.

Keep in mind that I was getting Segmentation Fault once in a while when trying to run tests. Simply try again. If it persists - try npm i and then try re-running the test. This always fixed it for me.

dmitryr117 commented 4 years ago

Hello @NathanWalker .

It seems that there is currently an issue with a conflict when running testing out of the box. /libs and /xplat are configured inside /testing folder to use karma, while nx workspace is configured to use Jest. As a result running yarn run affected:test out of the box produces a bunch of dependency errors. I am trying to make if work with Jest across the board. However there are still some issues that I am running into. None the less you can see some comparison progress here: https://github.com/nstudio/xplat/compare/master...dmitryr117:master I temporarily set version to 9.10.12 because my verdaccio local reg was not cooperating. It will be removed later. I also ran yarn test to test xplat itself. The tests that failed had to do with karma.conf.js file missing. It was removed and replaced with Jest files instead.

Current progress

Also there is a problem when generating angular web app. It installs "jest-preset-angular": "7.0.0" instead of 8.0.0. Can't find where that is. 8 is required otherwise jest breaks. Also jest-jasmine2 package needs to be installed into dev dependency as well.

/lib folder default tests run fine. /xplat folder default tests seem to have issues unable to load required code. Didn't have time to find the cause yet. /apps/testapp default tests for apps fail to find workspace locations and load required code from libs and xplat. Didn't have time to determine the cause yet. Simple tests pass ok.

Any input would be appreciated. Thanks.

NathanWalker commented 4 years ago

Thank you @dmitryr117 for all of this. The PR posted will allow me to help best as can run it from state you have things. Looking forward to wrapping this up and will let you know further on that pr 👍

draylegend commented 4 years ago

ERROR: Specify the project name and the target (e.g., nx run proj:build)

What I've done:

npx create-nx-workspace --preset=empty --cli=nx npm install --save-dev @nstudio/xplat nx generate @nstudio/xplat:application --name=Client --framework=angular --platforms=web --setupSandbox --useXplat npm run test

I know, that I can use nx test web-client, but if I use NX only it does run all tests/linting across all projects & libs with the command npm run test or npm run lint.

The question is: how can I lint & test all projects & libs with a command like npm run lint && npm run test? I'd like to use this command with husky.

dmitryr117 commented 4 years ago

@vladimirdrayling Generally I use npm run affected:test. It should work then. npm run test I remmember was not working properly for some reason.

wSedlacek commented 3 years ago

@dmitryr117 Did you ever write that blog post? I have a few question about unit testing with NativeScript and jest.

In particular,