Skip to content

Commit

Permalink
feat(breaking): Implement astTransformer compatible with ts-jest 23.10 (
Browse files Browse the repository at this point in the history
#204)

Closes #195 and closes #201

### Breaking Change
This is a breaking change as Configuration Interface changes ([see ts-jest v23.10 docs](https://kulshekhar.github.io/ts-jest/user/config/#ts-jest-options))

This PR is built upon @huafu's #201, kudos to him for setting up everything and pointing out what was still to be done. Transformer and test are also heavily inspired by his examples in ts-jest. Also see the PR for more information.

### Implementation Details
Like the Regex, all the transformer does is transform
* `templateUrl: <PATH>` to `template: require(<PATH>)`
* `styles: <ANYTHING>` to `styles: []` 
* `styleUrls <ANYTHING>` to `styleUrls: []`

For more information see the comments in the [transformer file](https://github.com/wtho/jest-preset-angular/blob/55bcfdb993d1dc511611553995ff566d64ea6944/src/InlineHtmlStripStylesTransformer.ts) or #201.

### Edge Cases
I had to make some decisions on how strict the astTransformer is, I decided for this, but I am open to discussion:
**Works**
```ts
import { Component as Cmp } from '@angular/core';
@cmp({
  templateUrl: './some-path.html'
})
class AngularComp { }
```
Caveat - this also gets transformed
```ts
@SomeOtherDecorator({
  templateUrl: './some-path.html'
})
class SomeClass { }
```

**Does not work**
```ts
const componentArgs = {
  templateUrl: './some-path.html'
}
@component(componentArgs)
class AngularComp { }
```

### Additional Notes
* Run unit tests for the transformer with `npm test`
* The transformer was written in TypeScript. A `prepare`-script for was added, which compiles it to JS before publishing and after `npm install`.
* `preprocessor.js` and its unit tests were removed.
  • Loading branch information
wtho authored and thymikee committed Dec 4, 2018
1 parent 7751de3 commit adad842
Show file tree
Hide file tree
Showing 24 changed files with 1,428 additions and 371 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
InlineHtmlStripStylesTransformer.js
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/example
/__tests__
/src
circle.yml
1 change: 0 additions & 1 deletion AngularSnapshotSerializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const printAttributes = (val, attributes, print, indent, colors, opts) => {
};

const print = (val, print, indent, opts, colors) => {
let result = '';
let componentAttrs = '';

const componentName = val.componentRef._elDef.element.name;
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
## Changelog (master)

* Breaking: Upgrade ts-jest to ^23.10.4 and use ast-transformer instead of processor ([#204](https://github.com/thymikee/jest-preset-angular/pull/204))
* Adjustment: Remove template literal character escaping (reverts [#34](https://github.com/thymikee/jest-preset-angular/pull/34))

#### Migration Guide

* If `global` and `transform` are not set in your configuration in `jest.config.json`, `jest.config.js` or `package.json`, you are done.
* If the `global` value of the configuration was overriden, adjust
* The option `"__TRANSFORM_HTML__": true` is not required anymore. Instead the `"stringifyContentPathRegex": "\\.html$"` should be used inside the `ts-jest`-configuration.
* Change the assignment identifier from `tsConfigFile` to `tsConfig`.
* Add the `astTransformer: [ require.resolve('jest-preset-angular/InlineHtmlStripStylesTransformer')]` so Jest can work with `templateUrl`-assignments in Component decorators.
* If `transform` was overridden, remove the entry pointing at `preprocessor.js` and add `"^.+\\.(ts|js|html)$": "ts-jest"` to the `transform`-object.
* If in doubt, check the configuration example in `jest-preset.json`.

### v6.0.1

* Fix: Support backtick quoted templateUrl. ([#182](https://github.com/thymikee/jest-preset-angular/pull/182))
Expand Down
46 changes: 27 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ yarn add -D jest jest-preset-angular
This will install `jest`, `@types/jest`, `ts-jest`, `jest-zone-patch` as dependencies needed to run with Angular projects.

## Usage

In `src` directory create `setupJest.ts` file with following contents:
```js

```ts
import 'jest-preset-angular';
import './jestGlobalMocks'; // browser mocks globally available for every test
```

*Note: feel free to copy the `jestGlobalMocks.ts` file from the example directory and save it next to the `setupJest.ts` file.*

...and include this in your `package.json`:

```json
{
"jest": {
Expand All @@ -40,29 +43,30 @@ import './jestGlobalMocks'; // browser mocks globally available for every test
{
"globals": {
"ts-jest": {
"tsConfigFile": "src/tsconfig.spec.json"
},
"__TRANSFORM_HTML__": true
"tsConfig": "<rootDir>/src/tsconfig.spec.json",
"stringifyContentPathRegex": "\\.html$"
}
},
"transform": {
"^.+\\.(ts|js|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js"
"^.+\\.(ts|js|html)$": "ts-jest"
},
"testMatch": [
"**/__tests__/**/*.+(ts|js)?(x)",
"**/+(*.)+(spec|test).+(ts|js)?(x)"
],
"moduleFileExtensions": [
"ts",
"js",
"html"
"**/?(*.)+(spec|test).+(ts|js)?(x)"
],
"moduleFileExtensions": ["js", "json", "jsx", "node", "ts", "tsx", "html"],
"moduleNameMapper": {
"app/(.*)": "<rootDir>/src/app/$1",
"assets/(.*)": "<rootDir>/src/assets/$1",
"environments/(.*)": "<rootDir>/src/environments/$1"
"^src/(.*)$": "<rootDir>/src/$1",
"^app/(.*)$": "<rootDir>/src/app/$1",
"^assets/(.*)$": "<rootDir>/src/assets/$1",
"^environments/(.*)$": "<rootDir>/src/environments/$1"
},
"transformIgnorePatterns": [
"node_modules/(?!@ngrx)"
],
"snapshotSerializers": [
"jest-preset-angular/AngularSnapshotSerializer.js",
"jest-preset-angular/HTMLCommentSerializer.js"
]
}
```
Expand Down Expand Up @@ -93,7 +97,7 @@ If you look at your `src/test.ts` (or similar bootstrapping test file) file you'
Example:

`calc-component.spec.ts`
```js
```ts
// some initialization code
test('renders markup to snapshot', () => {
const fixture = TestBed.createComponent(AppComponent);
Expand All @@ -120,7 +124,7 @@ exports[`CalcComponent should snap 1`] = `
`;
```

### Removing empty lines and white-spaces in component snaphots
### Removing empty lines and white-spaces in component snapshots

You will immediately notice, that your snapshot files contain a lot of white spaces and blank lines. This is not an issue with Jest, rather with Angular. It can be mitigated via Angular compiler by setting `preserveWhitespaces: false`

Expand Down Expand Up @@ -213,7 +217,7 @@ describe('Component snapshots', () => {

## Troubleshooting

Problems may arise if you're using custom builds (this preset is tailored for `angular-cli` as firsty priority). Please be adivsed that every entry in default configuration may be overriden to best suite your app's needs.
Problems may arise if you're using custom builds (this preset is tailored for `angular-cli` as firstly priority). Please be advised that every entry in default configuration may be overridden to best suite your app's needs.

### @Input() bindings are not reflected into fixture when `ChangeDetectionStrategy.OnPush` is used

Expand Down Expand Up @@ -254,15 +258,19 @@ Reference: https://github.com/angular/material2/issues/7101
### Absolute imports

TypeScript supports absolute imports. The preset (starting from v3.0.0) by default understands absolute imports referring to `src`, `app`, `assets` and `environments` directory, so instead:
```js

```ts
import MyComponent from '../../src/app/my.component';
import MyStuff from '../../src/testing/my.stuff';
```

you can use:
```js

```ts
import MyComponent from 'app/my.component';
import MyStuff from 'src/testing/my.stuff';
```

However, if your directory structure differ from that provided by `angular-cli` you can adjust `moduleNameMapper` in Jest config:

```json
Expand Down
141 changes: 141 additions & 0 deletions __tests__/InlineHtmlStripStylesTransformer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Code is inspired by
* https://github.com/kulshekhar/ts-jest/blob/25e1c63dd3797793b0f46fa52fdee580b46f66ae/src/transformers/hoist-jest.spec.ts
*
*/

import * as tsc from 'typescript'
import * as transformer from '../InlineHtmlStripStylesTransformer'

const CODE_WITH_TEMPLATE_URL = `
import { Component } from '@angular/core';
@Component({
templateUrl: './page.html'
})
export class AngularComponent {
}
`

const CODE_WITH_NON_RELATIVE_TEMPLATE_URL = `
import { Component } from '@angular/core';
@Component({
templateUrl: 'page.html'
})
export class AngularComponent {
}
`

const CODE_WITH_STYLE_URLS = `
import { Component } from '@angular/core';
@Component({
styleUrls: [
'./fancy-styles.css',
'./basic-styles.scss'
]
})
export class AngularComponent {
}
`

const CODE_WITH_STYLES = `
import { Component } from '@angular/core';
@Component({
styles: [
'body: { display: none }',
'html: { background-color: red }'
]
})
export class AngularComponent {
}
`

const CODE_WITH_ALL_DECORATOR_PROPERTIES = `
import { Component } from '@angular/core';
@SomeDecorator({
value: 'test'
})
@Component({
templateUrl: './page.html',
styleUrls: [
'./fancy-styles.css',
'./basic-styles.scss'
],
styles: [
'body: { display: none }',
'html: { background-color: red }'
],
unaffectedProperty: 'whatever'
})
@SomeOtherDecorator({
prop: 'ok'
})
export class AngularComponent {
}
`

const CODE_WITH_CUSTOM_DECORATOR = `
import { Component as CustomDecoratorName } from '@angular/core';
@CustomDecoratorName({
templateUrl: './page.html'
})
export class AngularComponent {
}
`



const createFactory = () => {
return transformer.factory({ compilerModule: tsc } as any)
}
const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } })

describe('inlining template and stripping styles', () => {
it('should have correct signature', () => {
expect(transformer.name).toBe('angular-component-inline-template-strip-styles')
expect(typeof transformer.version).toBe('number')
expect(transformer.version).toBeGreaterThan(0)
expect(typeof transformer.factory).toBe('function')
})

it('should strip styleUrl assignment', () => {
const out = transpile(CODE_WITH_STYLE_URLS)

expect(out.outputText).toMatchSnapshot()
})

it('should strip styles assignment', () => {
const out = transpile(CODE_WITH_STYLES)

expect(out.outputText).toMatchSnapshot()
})

it('should inline templateUrl assignment', () => {
const out = transpile(CODE_WITH_TEMPLATE_URL)

expect(out.outputText).toMatchSnapshot()
})

it('should inline non-relative templateUrl assignment and make it relative', () => {
const out = transpile(CODE_WITH_NON_RELATIVE_TEMPLATE_URL)

expect(out.outputText).toMatchSnapshot()
})

it('should handle all transformable decorator assignments', () => {
const out = transpile(CODE_WITH_ALL_DECORATOR_PROPERTIES)

expect(out.outputText).toMatchSnapshot()
})

it('should handle all decorator assignments in differently named decorators', () => {
const out = transpile(CODE_WITH_CUSTOM_DECORATOR)

expect(out.outputText).toMatchSnapshot()
})
})
Loading

0 comments on commit adad842

Please sign in to comment.