163 lines
5.6 KiB
Plaintext
163 lines
5.6 KiB
Plaintext
include ../../../../_includes/_util-fns
|
||
|
||
:marked
|
||
We’ll test an Angular pipe in this chapter
|
||
|
||
An Angular pipe is a declarative way in HTML to transform some input into some displayable output.
|
||
|
||
We’ll look at our app’s custom `InitCapsPipe` that converts a string of words into a string of capitalized words.
|
||
|
||
We use it our `hero-detail.component.html` template to turn a hero name like “eeny weenie” into “Eeny Weenie”
|
||
|
||
code-example(format="linenums" language="html" escape="html").
|
||
<h2>{{hero.name | initCaps}} is {{userName}}'s current super hero!</h2>
|
||
|
||
:marked
|
||
The code for `InitCapsPipe` in `init-caps-pipe.ts` is quite brief:
|
||
|
||
```
|
||
import {Pipe} from 'angular2/angular2';
|
||
|
||
@Pipe({ name: 'initCaps' })
|
||
export class InitCapsPipe {
|
||
transform(value: string) {
|
||
return value.toLowerCase().replace(/(?:^|\s)[a-z]/g, function(m) {
|
||
return m.toUpperCase();
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
In this chapter we will:
|
||
- add the Angular 2 library to our test harness
|
||
- test this custom Angular pipe class
|
||
- load multiple test files in our test harness, using system.js
|
||
|
||
.callout.is-helpful
|
||
header Prior Knowledge
|
||
:marked
|
||
The Unit Testing chapters build upon each other. We recommend reading them in order.
|
||
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
|
||
we introduced in the [QuickStart](../quickstart.html) and
|
||
the [Tour of Heroes](../tutorial/) tutorial
|
||
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
|
||
|
||
:marked
|
||
## Add the Angular library
|
||
Looking back at `unit-tests.html` we realize that we have not loaded the Angular library.
|
||
Yet we were able to load and test the application’s `Hero` class.
|
||
|
||
**We were lucky!** The `Hero` class has no dependence on Angular.
|
||
If it had depended on Angular, we’d still be staring at the Jasmine “big-time fail” screen:
|
||
|
||
figure.image-display
|
||
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
|
||
style="width:400px;" alt="Jasmine's' big time fail screen")
|
||
|
||
:marked
|
||
If we then opened the browser’s Developer Tools (F12, Ctrl-Shift-I) and looked
|
||
in the console window, we would see that SystemJS
|
||
tried to load Angular and couldn't find it.
|
||
|
||
code-example(format="" language="html" escape="html").
|
||
GET http://127.0.0.1:8080/src/angular2/angular2 404 (Not Found)
|
||
|
||
:marked
|
||
We are writing an Angular application afterall and
|
||
we were going to need Angular sooner or later. That time has come.
|
||
The `InitCapsPiep` clearly depends on Angular as is clear in the first few lines:
|
||
```
|
||
import {Pipe} from 'angular2/angular2';
|
||
|
||
@Pipe({ name: 'initCaps' })
|
||
export class InitCapsPipe {
|
||
```
|
||
**Open** `unit-tests.html`
|
||
|
||
**Find** the `src="../node_modules/systemjs/dist/system.src.js"></script>`
|
||
|
||
**Replace** Step #1 with these two scripts:
|
||
```
|
||
<!-- #1. add the system.js and angular libraries -->
|
||
<script src="../node_modules/systemjs/dist/system.src.js"></script>
|
||
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
|
||
```
|
||
## Add another spec file
|
||
|
||
**Create** an *`init-caps-pipe.spec.ts`** next to `init-caps-pipes.ts` in `src/app`
|
||
|
||
**Stop and restart the TypeScript compiler** to ensure we compile the new file.
|
||
|
||
**Add** the following lines of rather obvious Jasmine test code
|
||
```
|
||
import {InitCapsPipe} from './init-caps-pipe';
|
||
|
||
describe('InitCapsPipe', () => {
|
||
let pipe:InitCapsPipe;
|
||
|
||
beforeEach(() => {
|
||
pipe = new InitCapsPipe();
|
||
});
|
||
|
||
it('transforms "abc" to "Abc"', () => {
|
||
expect(pipe.transform('abc')).toEqual('Abc');
|
||
});
|
||
|
||
it('transforms "abc def" to "Abc Def"', () => {
|
||
expect(pipe.transform('abc def')).toEqual('Abc Def');
|
||
});
|
||
|
||
it('leaves "Abc Def" unchanged', () => {
|
||
expect(pipe.transform('Abc Def')).toEqual('Abc Def');
|
||
});
|
||
});
|
||
```
|
||
Note that each test is short (one line in our case).
|
||
It has a clear label that accurately describes the test. And it makes exactly one expectation.
|
||
|
||
Anyone can read these tests and understand quickly what the test does and what the pipe does.
|
||
If one of the tests fails, we know which expected behavior is no longer true.
|
||
We’ll have little trouble maintaining these tests and adding more like them as we encounter new conditions to explore.
|
||
|
||
That’s the way we like our tests!
|
||
|
||
## Add this spec to `unit-tests.html`
|
||
|
||
Now let’s wire our new spec file into the HTML test harness.
|
||
|
||
Open `unit-tests.html`. Find `System.import('app/hero.spec')`.
|
||
|
||
Hmm. We can’t just add `System.import('app/init-caps-pipe.spec')`.
|
||
|
||
The first `System.import` returns a promise as does this second import.
|
||
We can’t run any of the Jasmine tests until **both imports are finished**.
|
||
|
||
Fortunately, we can create a new `Promise` that wraps both import promises and waits
|
||
for both to finish loading.
|
||
```
|
||
// #3. Import the spec files explicitly
|
||
Promise.all([
|
||
System.import('app/hero.spec'),
|
||
System.import('app/init-caps-pipe.spec')
|
||
])
|
||
```
|
||
Try it. The browser should refresh and show
|
||
|
||
figure.image-display
|
||
img(src='/resources/images/devguide/testing-an-angular-pipe/5-specs-0-failures.png'
|
||
style="width:400px;" alt="import promises 5 specs, 0 failures")
|
||
|
||
:marked
|
||
We have a pattern for adding new tests.
|
||
|
||
In future, when we add a new spec, we add another `System.import('app/some.spec')` to
|
||
the array argument passed to `Promise.all`.
|
||
|
||
## What’s Next?
|
||
|
||
Now we can test parts of our application that we *load* asynchronously with system.js.
|
||
|
||
What about testing parts that *are themselves asynchronous*?
|
||
|
||
In the next chapter we’ll test a service with a public asynchronous method that fetches heroes
|
||
from a remote server. |