2016-02-06 02:27:06 -05:00
|
|
|
|
include ../_util-fns
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We’ll test an Angular pipe in this chapter
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
An Angular pipe is a declarative way in HTML to transform some input into some displayable output.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We’ll look at our app’s custom `InitCapsPipe` that converts a string of words into a string of capitalized words.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We use it our `hero-detail.component.html` template to turn a hero name like “eeny weenie” into “Eeny Weenie”
|
|
|
|
|
|
2015-10-16 04:06:56 -04:00
|
|
|
|
code-example(format="linenums" language="html" escape="html").
|
|
|
|
|
<h2>{{hero.name | initCaps}} is {{userName}}'s current super hero!</h2>
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2015-10-14 23:25:19 -04:00
|
|
|
|
The code for `InitCapsPipe` in `init-caps-pipe.ts` is quite brief:
|
|
|
|
|
|
|
|
|
|
```
|
2016-01-29 16:44:56 -05:00
|
|
|
|
import {Pipe} from 'angular2/core';
|
2015-10-14 23:25:19 -04:00
|
|
|
|
|
|
|
|
|
@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
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-10 06:29:34 -04:00
|
|
|
|
.callout.is-helpful
|
2015-10-14 23:25:19 -04:00
|
|
|
|
header Prior Knowledge
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2015-10-14 23:25:19 -04:00
|
|
|
|
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
|
2015-10-15 02:04:58 -04:00
|
|
|
|
the [Tour of Heroes](../tutorial/) tutorial
|
2015-10-14 23:25:19 -04:00
|
|
|
|
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2015-10-14 23:25:19 -04:00
|
|
|
|
## Add the Angular library
|
2015-11-10 13:31:46 -05:00
|
|
|
|
Looking back at `unit-tests.html` we realize that we have not loaded the Angular library.
|
2015-10-14 23:25:19 -04:00
|
|
|
|
Yet we were able to load and test the application’s `Hero` class.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
**We were lucky!** The `Hero` class has no dependence on Angular.
|
2015-10-14 23:25:19 -04:00
|
|
|
|
If it had depended on Angular, we’d still be staring at the Jasmine “big-time fail” screen:
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
|
|
|
|
figure.image-display
|
2015-11-10 13:31:46 -05:00
|
|
|
|
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
|
2015-10-14 23:25:19 -04:00
|
|
|
|
style="width:400px;" alt="Jasmine's' big time fail screen")
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
: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
|
2015-10-14 23:25:19 -04:00
|
|
|
|
tried to load Angular and couldn't find it.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-16 04:06:56 -04:00
|
|
|
|
code-example(format="" language="html" escape="html").
|
2016-01-29 16:44:56 -05:00
|
|
|
|
GET http://127.0.0.1:8080/src/angular2/core 404 (Not Found)
|
2015-11-10 13:31:46 -05:00
|
|
|
|
|
|
|
|
|
:marked
|
|
|
|
|
We are writing an Angular application afterall and
|
|
|
|
|
we were going to need Angular sooner or later. That time has come.
|
2015-10-14 23:25:19 -04:00
|
|
|
|
The `InitCapsPiep` clearly depends on Angular as is clear in the first few lines:
|
|
|
|
|
```
|
2016-01-29 16:44:56 -05:00
|
|
|
|
import {Pipe} from 'angular2/core';
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
@Pipe({ name: 'initCaps' })
|
|
|
|
|
export class InitCapsPipe {
|
|
|
|
|
```
|
|
|
|
|
**Open** `unit-tests.html`
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
**Find** the `src="../node_modules/systemjs/dist/system.src.js"></script>`
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
**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
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
**Create** an *`init-caps-pipe.spec.ts`** next to `init-caps-pipes.ts` in `src/app`
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
**Stop and restart the TypeScript compiler** to ensure we compile the new file.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
**Add** the following lines of rather obvious Jasmine test code
|
|
|
|
|
```
|
|
|
|
|
import {InitCapsPipe} from './init-caps-pipe';
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
describe('InitCapsPipe', () => {
|
|
|
|
|
let pipe:InitCapsPipe;
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
beforeEach(() => {
|
|
|
|
|
pipe = new InitCapsPipe();
|
|
|
|
|
});
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
it('transforms "abc" to "Abc"', () => {
|
|
|
|
|
expect(pipe.transform('abc')).toEqual('Abc');
|
|
|
|
|
});
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
it('transforms "abc def" to "Abc Def"', () => {
|
|
|
|
|
expect(pipe.transform('abc def')).toEqual('Abc Def');
|
|
|
|
|
});
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
it('leaves "Abc Def" unchanged', () => {
|
|
|
|
|
expect(pipe.transform('Abc Def')).toEqual('Abc Def');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
```
|
2015-11-10 13:31:46 -05:00
|
|
|
|
Note that each test is short (one line in our case).
|
2015-10-14 23:25:19 -04:00
|
|
|
|
It has a clear label that accurately describes the test. And it makes exactly one expectation.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
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.
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We’ll have little trouble maintaining these tests and adding more like them as we encounter new conditions to explore.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
That’s the way we like our tests!
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
## Add this spec to `unit-tests.html`
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
Now let’s wire our new spec file into the HTML test harness.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
Open `unit-tests.html`. Find `System.import('app/hero.spec')`.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
Hmm. We can’t just add `System.import('app/init-caps-pipe.spec')`.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
The first `System.import` returns a promise as does this second import.
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We can’t run any of the Jasmine tests until **both imports are finished**.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
Fortunately, we can create a new `Promise` that wraps both import promises and waits
|
2015-10-14 23:25:19 -04:00
|
|
|
|
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
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
|
|
|
|
figure.image-display
|
2015-11-10 13:31:46 -05:00
|
|
|
|
img(src='/resources/images/devguide/testing-an-angular-pipe/5-specs-0-failures.png'
|
2015-10-14 23:25:19 -04:00
|
|
|
|
style="width:400px;" alt="import promises 5 specs, 0 failures")
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
:marked
|
2015-10-14 23:25:19 -04:00
|
|
|
|
We have a pattern for adding new tests.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-11-10 13:31:46 -05:00
|
|
|
|
In future, when we add a new spec, we add another `System.import('app/some.spec')` to
|
2015-10-14 23:25:19 -04:00
|
|
|
|
the array argument passed to `Promise.all`.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
## What’s Next?
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
Now we can test parts of our application that we *load* asynchronously with system.js.
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2015-10-14 23:25:19 -04:00
|
|
|
|
What about testing parts that *are themselves asynchronous*?
|
2015-10-09 13:33:12 -04:00
|
|
|
|
|
2016-02-06 02:27:06 -05:00
|
|
|
|
Let's test a service with a public asynchronous method that fetches heroes
|
2015-10-14 23:25:19 -04:00
|
|
|
|
from a remote server.
|