Skip to content

Commit

Permalink
docs: add more troubleshooting docs (#129)
Browse files Browse the repository at this point in the history
* docs: Add guide how to mitigate whitespaces in snapshots

* docs: add troubleshooting solution for OnPush CD strategy

* chore(example-app): bump @angular and remove karma,jasmine

* feat(example-app): add @lib/testing module with helpers

* refactor(example-app): apply @lib/testing helper  for creating testbed in test

* feat(example-app): add onPush component test example

* docs: fix code example typos
  • Loading branch information
Hotell authored and thymikee committed Mar 10, 2018
1 parent e6c07fc commit c57a4b5
Show file tree
Hide file tree
Showing 16 changed files with 758 additions and 1,507 deletions.
131 changes: 122 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,126 @@ exports[`CalcComponent should snap 1`] = `
`;
```

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

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`

> By default it's set to `true` Angular 5.x, although it may change to be set to `false` in upcoming versions
> (if that occurs, you can stop reading right here, because your issue has been already solved)
Your `TestBed` setup should look like following:

```ts
describe('Component snapshot tests', ()=>{
// you need to turn TS checking because it's an private API
const compilerConfig = {preserveWhitespaces: false} as any

beforeEach(() => {
TestBed.configureCompiler(compilerConfig)
.configureTestingModule({...});
});

})
```

This is indeed very repetitive, so you can extract this in a helper function:

```ts
// test-config.helper.ts

import { TestBed } from '@angular/core/testing'

type CompilerOptions = Partial<{
providers: any[]
useJit: boolean
preserveWhitespaces: boolean
}>
export type ConfigureFn = (testBed: typeof TestBed) => void

export const configureTests = (configure: ConfigureFn, compilerOptions: CompilerOptions = {}) => {
const compilerConfig: CompilerOptions = {
preserveWhitespaces: false,
...compilerOptions,
}

const configuredTestBed = TestBed.configureCompiler(compilerConfig)

configure(configuredTestBed)

return configuredTestBed.compileComponents().then(() => configuredTestBed)
}
```

And setup your test with that function like following:

```ts
// foo.component.spec.ts

import { async, ComponentFixture } from '@angular/core/testing'

import { configureTests, ConfigureFn } from '../test-config.helper'

import { AppComponent } from './foo.component';

describe('Component snapshots', () => {

let fixture: ComponentFixture<FooComponent>;
let component: FooComponent;

beforeEach(
async(() => {
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [FooComponent],
imports: [...],
schemas: [NO_ERRORS_SCHEMA],
});
};

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

it(`should create snapshots without blank lines/white spaces`, () => {
expect(fixture).toMatchSnapshot();
});

})
```

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

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

This issue is not related to Jest, [it's a known Angular bug](https://github.com/angular/angular/issues/12313)

To mitigate this, you need to wrap your component under test, into some container component with default change detection strategy (`ChangeDetectionStrategy.Default`) and pass props through it, or overwrite change detection strategy within `TestBed` setup, if it's not critical for the test.

```ts
// override change detection strategy
beforeEach(
async(() => {
TestBed.configureTestingModule({ declarations: [PizzaItemComponent] })
.overrideComponent(PizzaItemComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default },
})
.compileComponents();
})
);
```

### The animation trigger "transformMenu" has failed
JSDOM missing transform property when using Angular Material, there is a workaround for it.

Add this to your `jestGlobalMocks` file
```

```js
Object.defineProperty(document.body.style, 'transform', {
value: () => {
return {
Expand All @@ -151,7 +262,8 @@ 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:
```js

```json
{
"jest": {
"moduleNameMapper": {
Expand All @@ -172,7 +284,7 @@ Override `globals` object in Jest config:
"globals": {
"ts-jest": {
"tsConfigFile": "src/tsconfig.custom.json"
},
},
"__TRANSFORM_HTML__": true
}
}
Expand All @@ -186,7 +298,8 @@ If you choose to overide `globals` in order to point at a specific tsconfig, you
This means, that a file is not transformed through TypeScript compiler, e.g. because it is a JS file with TS syntax, or it is published to npm as uncompiled source files. Here's what you can do.

#### Adjust your `transformIgnorePatterns` whitelist:
```js

```json
{
"jest": {
"transformIgnorePatterns": [
Expand All @@ -208,7 +321,7 @@ By default Jest doesn't transform `node_modules`, because they should be valid J
This tells `ts-jest` (a preprocessor this preset using to transform TS files) to treat JS files the same as TS ones.

#### Transpile js files through `babel-jest`
Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since `typescript` (and thus `ts-jest` used by this preset) do not transpile them.
Some vendors publish their sources without transpiling. You need to say jest to transpile such files manually since `typescript` (and thus `ts-jest` used by this preset) do not transpile them.
1. Install `babel-preset-env` and add `.babelrc` (or modify existing if needed) with that contents:
```
{
Expand Down Expand Up @@ -248,17 +361,17 @@ import './jestGlobalMocks';

The same like normal Jest configuration, you can load jQuery in your Jest setup file. For example your Jest setup file is `setupJest.ts` you can declare jQuery:

```
```js
window.$ = require('path/to/jquery');
```

or
or

```
```js
import $ from 'jquery';
global.$ = global.jQuery = $;
```

The same declaration can be applied to other vendor libraries.

Reference: https://github.com/facebook/jest/issues/708
41 changes: 17 additions & 24 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,37 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.2",
"@angular/common": "^5.2.2",
"@angular/compiler": "^5.2.2",
"@angular/core": "^5.2.2",
"@angular/forms": "^5.2.2",
"@angular/http": "^5.2.2",
"@angular/platform-browser": "^5.2.2",
"@angular/platform-browser-dynamic": "^5.2.2",
"@angular/router": "^5.2.2",
"@angular/animations": "^5.2.8",
"@angular/common": "^5.2.8",
"@angular/compiler": "^5.2.8",
"@angular/core": "^5.2.8",
"@angular/forms": "^5.2.8",
"@angular/http": "^5.2.8",
"@angular/platform-browser": "^5.2.8",
"@angular/platform-browser-dynamic": "^5.2.8",
"@angular/router": "^5.2.8",
"core-js": "^2.5.3",
"rxjs": "^5.5.6",
"zone.js": "^0.8.20"
},
"devDependencies": {
"@angular/cli": "^1.6.6",
"@angular/compiler-cli": "^5.2.2",
"@angular/cli": "^1.7.3",
"@angular/compiler-cli": "^5.2.8",
"@types/node": "~6.0.60",
"codelyzer": "^4.1.0",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"jest": "^22.1.4",
"jest-preset-angular": "^5.0.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"codelyzer": "^4.2.1",
"jest": "^22.4.2",
"jest-preset-angular": "^5.2.1",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
"typescript": "2.6.2"
},
"jest": {
"preset": "jest-preset-angular",
"setupTestFrameworkScriptFile": "<rootDir>/src/setupJest.ts",
"moduleNameMapper": {
"\\.(jpg|jpeg|png)$": "<rootDir>/__mocks__/image.js"
"\\.(jpg|jpeg|png)$": "<rootDir>/__mocks__/image.js",
"@lib/(.*)": "<rootDir>/src/lib/$1"
}
}
}
12 changes: 2 additions & 10 deletions example/src/app/__snapshots__/app.component.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,24 @@ exports[`AppComponent snaps 1`] = `
<h1
class="ng-tns-c1-0"
>
app works!
app works!
<app-calc
class="ng-tns-c1-0"
/>
<span
class="ng-tns-c1-0"
>
aaa $1234
</span>
<span
class="ng-tns-c1-0 ng-star-inserted"
style=""
>
ddd
</span>
\`test'chars""
\`test'chars""
</h1>
</app-root>
`;
41 changes: 24 additions & 17 deletions example/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {TestBed, async, fakeAsync, tick} from '@angular/core/testing';
import {NO_ERRORS_SCHEMA} from '@angular/core';
import {AppComponent} from './app.component';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { async, fakeAsync, tick, ComponentFixture } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';

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

import { AppComponent } from './app.component';

describe('AppComponent', () => {
let fixture;
let fixture: ComponentFixture<AppComponent>;
let component: AppComponent;

beforeEach(
async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [NoopAnimationsModule],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
const configure: ConfigureFn = testBed => {
testBed.configureTestingModule({
declarations: [AppComponent],
imports: [NoopAnimationsModule],
schemas: [NO_ERRORS_SCHEMA],
});
};

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

beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
});

it(
'should create the app',
async(() => {
const app = fixture.debugElement.componentInstance;
const app = component;
expect(app).toBeTruthy();
})
);
Expand All @@ -36,7 +43,7 @@ describe('AppComponent', () => {
it(
`should have as title 'app works!'`,
async(() => {
const app = fixture.debugElement.componentInstance;
const app = component;
expect(app.title).toEqual('app works!');
})
);
Expand Down
4 changes: 3 additions & 1 deletion example/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { CalcComponent } from './calc/calc.component';
import { SimpleComponent } from './simple/simple.component';
import { OnPushComponent } from './on-push/on-push.component';

@NgModule({
declarations: [
AppComponent,
CalcComponent,
SimpleComponent
SimpleComponent,
OnPushComponent
],
imports: [
BrowserModule,
Expand Down
11 changes: 2 additions & 9 deletions example/src/app/calc/__snapshots__/calc.component.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,12 @@ exports[`CalcComponent should snap 1`] = `
image={[Function String]}
prop1={[Function Number]}
>
<p
<p
class="a-default-class"
ng-reflect-klass="a-default-class"
ng-reflect-ng-class="[object Object]"
>
calc works!
1337
another text node
test-image-stub
calc works! 1337 another text node test-image-stub
</p>
</app-calc>
`;
Loading

0 comments on commit c57a4b5

Please sign in to comment.