Skip to content

Commit

Permalink
feat(babel): alternative setup babel/TS (thymikee#311)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: add an alternative preset for babel and restructures
the whole repository to treat babel and ts-jest equally. Furthermore
`ts-jest` is not a dependency of the preset anymore.

The jest config and package.json of users will have to be adjusted, see
the CHANGELOG.md for migration information.

Closes thymikee#308
  • Loading branch information
wtho committed Dec 2, 2019
1 parent f925564 commit e326cb6
Show file tree
Hide file tree
Showing 32 changed files with 1,987 additions and 111 deletions.
16 changes: 12 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,27 @@ jobs:
at: .
# skipping cache here as it created issues with the preset in the root
# not being used, as older version in cache was available
- run: 'sudo yarn global add greenkeeper-lockfile@2'
- run: 'greenkeeper-lockfile-update'
- run:
name: Greenkeeper - update lockfile
command: |
sudo yarn global add greenkeeper-lockfile@2
greenkeeper-lockfile-update
- run:
name: Install Example Dependencies
command: |
cd example
yarn install --frozen-lockfile
- run:
name: Test Example
name: Test Example - ts-jest preset
command: |
cd example
yarn test:ci
yarn test:ci:ts-jest
yarn test:coverage
- run:
name: Test Example - babel preset
command: |
cd example
yarn test:ci:babel
workflows:
version: 2
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Changelog (master)

#### Features
* (**BREAKING**): Add preset `babel` as transpilation alternative to `ts-jest` and remove `ts-jest` as dependency ([#317](https://github.com/thymikee/jest-preset-angular/pull/317)).

#### Migration Guide
* The preset reference in the jest config has to be updated from `"jest-preset-angular"` to `"jest-preset-angular/build/ts-jest"`. To use babel, replace `ts-jest` with `babel`. Furthermore, when using `ts-jest`, it has to be added as `devDependency` to the project, or `babel` packages alternatively (see README.md).

### v8.0.0

#### Features
Expand Down
56 changes: 53 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ This is a part of the article: [Testing Angular faster with Jest](https://www.xf

_Note: This preset does not suport AngularJS (1.x). If you want to set up Jest with AngularJS, please see [this blog post](https://medium.com/aya-experience/testing-an-angularjs-app-with-jest-3029a613251)._

This package actually contains two presets, one for `ts-jest`, which uses the official TypeScript compiler internally, and one for `babel`, which strips away the type annotations and simply transforms the JavaScript. If you are not sure, try to use `ts-jest`, to also type-check your test files.


## Installation

```bash
Expand All @@ -19,6 +22,26 @@ npm install -D jest jest-preset-angular @types/jest

This will install `jest`, `@types/jest`, `ts-jest` as dependencies needed to run with Angular projects.

Additionally you need `ts-jest` or `babel` packages.

### ts-jest
```bash
yarn add -D ts-jest
#or
npm install -D ts-jest
```

`ts-jest` uses the TypeScript compiler to generate JavaScript.

### babel
```bash
yarn add -D @babel/core @babel/preset-typescript @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators babel-plugin-transform-typescript-metadata babel-plugin-const-enum
#or
npm install -D @babel/core @babel/preset-typescript @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators babel-plugin-transform-typescript-metadata babel-plugin-const-enum
```

`babel` uses the babel compiler to generate JavaScript, without type-checking your test files before doing so. You can still do type-checking using `tsc --noEmit`. For additional TypeScript-supported language features you might have to install even more babel packages. Note that `babel` also can differ slightly from `tsc`, e. g. in compiling a class to a function.

## Usage

In `src` directory create `setupJest.ts` file with following contents:
Expand All @@ -35,17 +58,42 @@ _Note: feel free to copy the [`jestGlobalMocks.ts`](https://github.com/thymikee/
```json
{
"jest": {
"preset": "jest-preset-angular",
"preset": "jest-preset-angular/build/ts-jest",
"setupFilesAfterEnv": ["<rootDir>/src/setupJest.ts"]
}
}
```

or for `babel`:
```json
{
"jest": {
"preset": "jest-preset-angular/build/babel",
"setupFilesAfterEnv": ["<rootDir>/src/setupJest.ts"]
}
}
```

When using babel, also a `babel.config.js` on the project root level is required:
```js
const { babelAngularConfig } = require('jest-preset-angular/build/babel/babel.config')
module.exports = api => {
api.cache(false)
return {
presets: babelAngularConfig.presets,
plugins: [
...babelAngularConfig.plugins,
// additional plugins
]
}
}
```

### Avoid karma conflicts
By Angular CLI defaults you'll have a `src/test.ts` file which will be picked up by jest. To circumvent this you can either rename it to `src/karmaTest.ts` or hide it from jest by adding `<rootDir>/src/test.ts` to jest `testPathIgnorePatterns` option.


## Exposed [configuration](https://github.com/thymikee/jest-preset-angular/blob/master/jest-preset.js)
## Exposed [`ts-jest` configuration](https://github.com/thymikee/jest-preset-angular/blob/master/src/ts-jest/jest-preset.js)

```js
module.exports = {
Expand Down Expand Up @@ -78,11 +126,13 @@ module.exports = {
};
```

Note that the [`babel` configuration](https://github.com/thymikee/jest-preset-angular/blob/master/src/babel/jest-preset.js) looks different.

### Brief explanation of config

- `<rootDir>` is a special syntax for root of your project (here by default it's project's root /)
- we're using some `"globals"` to pass information about where our tsconfig.json file is that we'd like to be able to transform HTML files through ts-jest
- `"transform"` – run every TS, JS, or HTML file through so called _preprocessor_ (we'll get there); this lets Jest understand non-JS syntax
- `"transform"` – run every TS, JS, or HTML file through so called _preprocessor_, in our case just `ts-jest` or `babel-jest`; this lets Jest understand non-JS syntax
- `"testMatch"` – we want to run Jest on files that matches this glob
- `"moduleFileExtensions"` – our modules are TypeScript and JavaScript files
- `"moduleNameMapper"` – if you're using absolute imports here's how to tell Jest where to look for them; uses regex
Expand Down
12 changes: 12 additions & 0 deletions example/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { babelAngularConfig } = require('jest-preset-angular/build/babel/babel.config')
module.exports = api => {
api.cache(false)
return {
presets: babelAngularConfig.presets,
plugins: [
...babelAngularConfig.plugins,
// your plugins
]
}
}

11 changes: 11 additions & 0 deletions example/get-test-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const defaultTestMode = 'ts-jest';
const envTestModeKey = 'JPA_TEST_MODE';

module.exports = {
getTestMode() {
if (process.env && envTestModeKey in process.env && typeof process.env[envTestModeKey] === 'string') {
return process.env[envTestModeKey];
}
return defaultTestMode;
}
}
4 changes: 3 additions & 1 deletion example/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { getTestMode } = require('./get-test-mode');

module.exports = {
preset: "jest-preset-angular",
preset: `jest-preset-angular/build/${getTestMode()}`,
snapshotSerializers: [
"jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js",
"jest-preset-angular/build/AngularSnapshotSerializer.js",
Expand Down
17 changes: 15 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
"build": "ng build",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --runInBand",
"test:coverage": "jest --coverage"
"test:ci": "yarn test:ci:ts-jest && yarn test:ci:babel",
"test:ci:babel": "cross-env JPA_TEST_MODE=babel jest --runInBand",
"test:ci:ts-jest": "cross-env JPA_TEST_MODE=ts-jest jest --runInBand",
"test:coverage": "cross-env JPA_TEST_MODE=ts-jest jest --coverage"
},
"private": true,
"dependencies": {
Expand All @@ -30,10 +32,21 @@
"@angular/cli": "~8.3.4",
"@angular/compiler-cli": "~8.2.5",
"@angular/language-service": "~8.2.5",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/plugin-proposal-decorators": "^7.6.0",
"@babel/preset-env": "^7.6.2",
"@babel/preset-typescript": "^7.6.0",
"@types/jest": "^24.0.0",
"@types/node": "^11.0.0",
"babel-jest": "^24.9.0",
"babel-plugin-const-enum": "^0.0.2",
"babel-plugin-transform-typescript-metadata": "^0.2.2",
"cross-env": "^6.0.3",
"jest": "^24.0.0",
"jest-preset-angular": "file:../",
"prettier": "^1.18.2",
"ts-jest": "^24.1.0",
"ts-node": "^8.0.3",
"typescript": "~3.5.3"
}
Expand Down
15 changes: 0 additions & 15 deletions example/src/app/calc/__snapshots__/calc.component.spec.ts.snap

This file was deleted.

59 changes: 43 additions & 16 deletions example/src/app/calc/calc.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture } from '@angular/core/testing';

import { configureTests, ConfigureFn } from '@lib/testing';

Expand All @@ -8,23 +8,50 @@ describe('CalcComponent', () => {
let component: CalcComponent;
let fixture: ComponentFixture<CalcComponent>;

beforeEach(
async(() => {
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [CalcComponent]
});
};

configureTests(configure).then(testBed => {
fixture = testBed.createComponent(CalcComponent);
component = fixture.componentInstance;
fixture.detectChanges();
beforeEach(async(() => {
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [CalcComponent]
});
})
);
};

configureTests(configure).then(testBed => {
fixture = testBed.createComponent(CalcComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
}));

it('should snap', () => {
expect(fixture).toMatchSnapshot();
if (global['JpaTestMode'] === 'ts-jest') {
expect(fixture).toMatchInlineSnapshot(`
<app-calc
hasAClass="false"
image={[Function String]}
prop1={[Function Number]}
>
<p
class="a-default-class"
>
calc works! 1337 another text node test-image-stub
</p>
</app-calc>
`);
} else {
expect(fixture).toMatchInlineSnapshot(`
<app-calc
hasAClass="false"
image={[Function String]}
observable$="undefined"
prop1={[Function Number]}
>
<p
class="a-default-class"
>
calc works! 1337 another text node test-image-stub
</p>
</app-calc>
`);
}
});
});
31 changes: 31 additions & 0 deletions example/src/app/hero-category/hero-category.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from "@angular/core";
import { HeroCategory, HeroColor } from "./hero-properties";

@Injectable({
providedIn: "root"
})
export class HeroCategoryService {
getCategoryForHero(heroName: string): HeroCategory {
switch (heroName) {
case "Joker":
return HeroCategory.Evil;
case "Batman":
return HeroCategory.Good;
default:
return HeroCategory.Neutral;
}
}

getColorForHero(heroName: string): HeroColor {
switch (heroName) {
case "Joker":
return HeroColor.Purple;
case "Batman":
return HeroColor.Black;
case "Catwoman":
return HeroColor.Black;
default:
return HeroColor.Transparent;
}
}
}
27 changes: 27 additions & 0 deletions example/src/app/hero-category/hero-category.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { async } from "@angular/core/testing";

import { HeroCategoryService } from "./hero-category.service";
import { HeroCategory, HeroColor } from './hero-properties';

describe("HeroCategoryService", () => {
let service: HeroCategoryService;

beforeEach(() => (service = new HeroCategoryService()));

it("should create", () => {
expect(service).toBeTruthy();
});

it("should return the right category for heroes", () => {
expect(service.getCategoryForHero("Batman")).toEqual(HeroCategory.Good);
expect(service.getCategoryForHero("Joker")).toEqual(HeroCategory.Evil);
expect(service.getCategoryForHero("Catwoman")).toEqual(HeroCategory.Neutral);
});

it("should return the right color for heroes", () => {
expect(service.getColorForHero("Batman")).toEqual(HeroColor.Black);
expect(service.getColorForHero("Joker")).toEqual(HeroColor.Purple);
expect(service.getColorForHero("The Penguin")).toEqual(HeroColor.Transparent);
});

});
12 changes: 12 additions & 0 deletions example/src/app/hero-category/hero-properties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

export enum HeroCategory {
Evil,
Good,
Neutral
}

export const enum HeroColor {
Black = "#000000",
Purple = "#551A8B",
Transparent = "#00000000"
}
4 changes: 4 additions & 0 deletions example/src/setupJest.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
import 'jest-preset-angular';
import './jestGlobalMocks';

// for example app testing only
import { getTestMode } from '../get-test-mode';
global['JpaTestMode'] = getTestMode()
Loading

0 comments on commit e326cb6

Please sign in to comment.