Monthly Archives: July 2017

A web animations deep dive with Angular

button state machine

Motion is an important aspect when building modern web applications. In fact, it’s important for all kinds of software products that involve user interfaces and interactions. Good user interfaces with well-designed animations help users understand the flow between two states. Imagine we are on a simple website that only has one button. We click that button and without any motion a box appears. Isn’t that boring? Also, we as a user might think that the box appeared because of our action. However, it could have been the result of something else like an http call in the background. In addition, animations can be used to make the user interface more snappy and responsive. They also explain changes in the arrangement of elements on the screen as some user actions may change the UI.

This could easly drift off into a whole new discussion about user experience and why motion matters. The bottom line is that motion not only makes a site more usable but also more fun. They tell stories, add a perceptible time dimension and improve the overall user experience of applications.

In this article we’ll briefly look at different ways to approach motion in modern web applications, specifically imperative and declarative animations. We’ll cover the basics of CSS and JavaScript animations before diving diving into a sophisticated animation in the context of an Angular application.

Let’s start with a deeper breath of how animations in the web generally work.

Understanding state transitions

With animations we aim to guide users between views so they feel comfortable using the site, draw attention to some parts of application, increase spacial awareness, indicate if data is being loaded, and probably the most important point – smoothly transition users between states. All this could be achieved by using CSS (declarative) or JavaSript (mostly imperative) to animate certain elements within your page.

What’s a transition you may ask. Very good question! The Oxford Dictionary defines a transition as follows:

The process or a period of changing from one state or condition to another.

Applied to animations, a transition is the visualization of some state changing over time. A state could be a person sitting at the airport waiting for his plane to be boarded. It’s a condition something or someone is in at a specific point in time. A button on website could have 4 different states – idle, hover, focus and pressed, where the latter is a combination of focus and active. We could use an infinite state machine or simply state transition system to visualize how it works:

The point is, a “system” or some element on the page can have multiple states. Instead of simply going from state A to B, we’d like to interpolate the values in between. Later in this post we’ll see how to use Angular’s animation system to implement a rich profile animation. For this it is necessary to understand the concept of states and state machines as it is kind of implemented as one. With transitions we can listen for state changes and act accordingly.

What can we use to animate our UI?

Nowadays, with modern browsers in mind, we have many technologies at hand to animate our UI including CSS3 or JavaScript. The power of CSS animations (transitions or keyframes) is that they allow animating most HTML elements without using JavaScript. CSS animations are also quite fast and allow hardware acceleration. However, there are some limits to this approach. For instance, you can’t animate an element along a certain path, use physics-based motion or animate the scroll position with CSS alone. While CSS animations are pretty good for simple transitions between states (e.g. hover-effects), JavaScript-based animations provide far more flexibility.

As a matter of fact, we can take advantage of the same hardware acceleration in JavaSript too. It’s as easy as setting a CSS property with a 3D characteristic, such as translate3d() or matrix3d(). This will push the particular element onto another layer which is then processed by the GPU. The GPU itself is highly optimized for moving pixels making it much for effective for animations compared to the CPU. For more information, check out this great article by Paul Lewis and Paul Irish on high performance animations.

CSS animations do not require any 3rd party libraries. However, there are some tools that make your life much easier, for example libraries that provide pre-defined keyframe animations like Animate.css.

Looking at JavaScript, we can either use vanilla JavaScript or jQuery to animate our UI. Maintaining vanilla JavaScript animations and manually setting up elements on the stage could be quite a hassle. That’s one reason why many of us switched to jQuery at some point. jQuery makes it easy to query an element on the page. Then, in order to add motion, all we do is to call .animate() and specify properties (e.g. opacity or transform) we’d like to animate. Here is how we’d move a div to the right by 200px animating its left property:

$("button").click(function(){
  $("div").animate({
    left: '200px'
  }, 'slow');
});

While this works, it’s better to stick with either the transform or opacity property as those are the only things a browser can animate cheaply. Note that we use the string slow to specify the duration of the animation. It is an equivalent for supplying a duration of 600 milliseconds.

Turns out there is another cool kid on the block called GreenSock, a high-performance HTML5 animation library for the modern web. It allows us to define simple animations as well as complex timeline compositions, drag and drop features or even smoothly morph any SVG shape into any other shape. According to GreenSock, GSAP performs 20 times faster than jQuery. If you’d like to see this in action, there is a speed comparison to stress test the performance of various common JavaScript libraries including jQuery, GSAP or Web Animations.

Let’s create the same animation as before, but this time using GSAP. More specifically we’ll use TweenLite, a lightweight animation tool that serves as the foundation of GSAP.

var button = document.querySelector('button');

button.addEventListener('click', () => {
  TweenLite.to('div', 0.6, {
    left: 200
  });
});

Note that the code above requires a plugin called CSSPlugin. This plugin allows us to animate almost any CSS property.

When working with frontend frameworks like Angular it could be a whole new story as some of them handle animations differently.

That doesn’t mean it’s not using some of the core concepts under the hood. Tthey often come with their own animation system. Take Angular’s animation system for example, that is built on top of the Web Animations API (WAAPI). It a fairly new and currently being implemented in Chrome and Firefox. It’s goal is to unite CSS, JS and SVG. More specifically, it aims to provide the power of CSS performance, add the benefits and flexibility of JS and SVG as well as leave the fundamental work to the browser without the need for additional dependencies.

If you want to learn more about the Web Animation API, check out this series of posts which goes a lot more into detail while covering advanced features like running multiple animations in parallel or in sequence, animating elements along a motion path or controlling animations using the AnimationPlayer.

Here is a snippet showing the WAAPI in action:

var button = document.querySelector('button');

var wrapper = document.querySelector('div');
wrapper.style.position = 'relative';

button.addEventListener('click', () => {
  wrapper.animate([
    { left: getComputedStyle(elem).left },
    { left: '200px' }
  ], { duration: 600, /* and more like easing, delay etc. */ });
});

Remember, the WAAPI is still a work in progress and things like additive animations are not fully supported yet. That’s why we use getComputedStyle() to calculate the very first KeyframeEffect. A KeyframeEffect is used to specify the values for the properties we’d like to animate. Each effect represents one keyframe and the values are basically interpolated over time. In other words, the array is a collection of keyframes. Here is an equivalent CSS keyframe animation:

@keyframes moveToRight {
  from {
    left: 0px;
  }
  to {
    left: 200px;
  }
}

div {
  position: relative;
  animation: moveToRight 600ms forwards;
}

Similar to the WAAPI, we also need to set the initial value when animating the left property. This is not needed if we were translating the element on the X-axis to another location via its transform property. With CSS keyframe animations we normally define when the change will happen with either percentage values or the keywords from and to, which are the same as 0% and 100%.

We do this with the WAAPI by defining an offset for each set of property values (keyframe). Keyframes without any offset will have offsets computed, e.g. the first keyframe has an offset of 0, the last will be 1.

So far, we have seen a little bit of CSS and JavaScript animations and how to leverage them to animate parts of our application. In the next section we’ll look at at a specific case study implementing a real-world user profile animation. The goal is to implement the exact same animation both imperatively and declaratively using GSAP and Angular’s built-in animation system. Yes, there’s going to be a lot of Angular!

Case study: An animated modal-based user profile

Enough about theory! Let’s build a simple modal-based user profile and apply animations to improve the user experience and draw focused-attention to the dialog.

Here’s a preview of what we are going to build:

animation preview

Our application is going to be very simple and mainly consists of two components:

  • DashboardComponent
  • ProfileDetailsComponent

The DashboardComponent is the entry point (root component) of our application. It contains almost no logic and merely represents a wrapper composing the ProfileDetailsComponent.

Inside the DashboardComponent we initialize the date for the user profile and toggle the visibility of the dialog. An ngIf will then show and hide the template. This is important for our animation because we use it as a trigger.

Here is the template of the DashboardComponent:

<div>
  <header>
    <span class="title">Dashboard</span>
    <div class="image-container" (click)="toggleProfileDetails()" data-tooltip="Profile" >
      <img class="profile-button" src="..." />
    </div>
  </header>

  <profile-details [user]="user" *ngIf="showProfileDetails"></profile-details>
</div>

So far so good. Let’s look at the template of ProfileDetailsComponent:

<div class="wrapper">
  <header>
    <div class="profile-image-wrapper">
      <div class="profile-image-border"></div>
      <img class="profile-image" src="..." />
    </div>
    <div class="profile-header-content">
      <span class="username">{{ user.name }}</span>
      <span class="username-title">{{ user.title }}</span>
    </div>
  </header>
  <main>
    <ul class="stats">
      <li class="stats-item">
        <span class="stats-icon icon-eye"></span>
        <span>{{ user.views }}</span>
      </li>
      <li class="stats-item">
        <span class="stats-icon icon-location"></span>
        <span>{{ user.location }}</span>
      </li>
      <li class="stats-item">
        <span class="stats-icon icon-heart"></span>
        <span>{{ user.hearts }}</span>
      </li>
    </ul>
  </main>
</div>

To achieve the desired animation we need to set some initial CSS properties to enable 3D-space for the children elements inside ProfileDetailsComponent. We do that by setting the perspective on the host element. CSS host selectors are a great way to apply styles without introducing an additional wrapper element.

Nonetheless, for our animation we still need a wrapper element because the perspective property doesn’t affect how the host element is rendered; it simply enables 3D-space for children elements.

:host {
  perspective: 500px;
  ...
}

.wrapper {
  transform-origin: top center;
  ...
}

Again, the perspective only affects children elements and only those that are transformed in a three-dimensional space, e.g. rotation about X-axis or translation along Z-axis. The value for the perspective determines the strength of the 3D-effect. In other words, it describes the distance between the object and the viewer. Therefore, if the value is very small the effect will be quite impressive as we are extremely close to the object. On the other hand, if the value is high the distance between the object and the viewer will be large and therefore the animation looks rather subtle. That said, we need to set the perspective property in order to achieve 3D-effects.

In addition, we have to specify a point of origin for our upoming transformation. By default the point of origin is exactly the center of any element. The rest is just simple styles for the dialog.

Alright. Now that we got this in place, let’s implement our animation imperatively using GreenSocks’s timeline feature.

Imperative implementation using GreenSock

In order to achieve this animation with GSAP we need to use its timeline feature. Make sure to either use TweenMax or add TimelineLite separately to your project.

A timeline is basically a container where we place tweens over the course of time. Tweening is the process of generating intermediate frames between two states. With GSAP’s timeline we can easily build sequences of animations and animate an element .to() or .from() a certain state. In addition, we get a lot of control over our animations. As such, we can stop, pause, resume, or even reverse them. Here is a simple example:

window.onload = function () {
  var timeline = new TimelineLite();

  var h1 = document.getElementById('first');

  timeline
    .add('start')
    .from(h1, 0.7, { opacity: 0, ease: Power2.easeIn }, 'start')
    .from(h1, 1, { x: 200, ease: Bounce.easeOut }, 'start')
    .to('#second', 0.3, { backgroundColor: '#ffeb3b' })
    .to('#third', 0.3, { x: 200, repeat: 1, yoyo: true }, '-=0.3')
    .play();

  var button = document.getElementsByTagName('button');

  button[0].addEventListener('click', function() {
    timeline.restart();
  });
}

Check out the demo and try it out!

With TimelineLite we have complete control over where tweens are placed on the timeline and they can overlap as much as we want. Notice how we use .add() to add a label to the timeline. We can use labels to start multiple animations at the same time. For instance, we use this mechanism to run two animations in parallel. The h1 will fade and translate in at the same time. Both animations could easily be combined in a single animation, but they have different easing functions. It solely demonstrates how to use labels.

Let’s see how we can do that in our Angular application. First off, we get all the elements using Angular’s built-in @ViewChild() and @ViewChildren() decorators. We leverage those to query specific elements within the view of a component.

@ViewChild() returns an ElementRef, whereas @ViewChildren() returns a QueryList. Essentially it’s an object that stores a list of elements and implements the iterable interface. This makes it possible to be used in combination with ngFor. The cool thing is that it’s based on Observables. This means we can subscribe to changes and get notified whenever an element is added, removed, or moved.

For more information check out Minko’s blog post about the difference between view children and content children in Angular.

Here’s how we use it in our Angular application to grab the elements we need:

@ViewChild('wrapper') wrapper: ElementRef;
@ViewChild('main') main: ElementRef;
...

The decorator takes either a type or a template reference variable. In most cases, such template reference variable is a reference to a DOM element inside a component’s template. The following snippet shows how we’d get a reference to the wrapper element:

<div class="wrapper" #wrapper>
...
</div>

See the #wrapper? That’s how we declare a local template reference for that specific element. We do this for all the elements we need for the animation. With that in place, we can instantiate our timeline.

Usually we use ngOnInit to implement our initialization logic. However, this is a little bit too early in a component’s lifecycle because we have to wait for the component to be fully initialized in order to use the DOM elements we collected. There’s a lifecycle hook called ngAfterViewInit which is the perfect moment in a component’s initialization process in which we have everything we need to set up the timeline.

ngAfterViewInit() {
  this.timeline = new TimelineLite();
  ...
}

Cool! But before we can construct the timeline for our profile animation there’s one thing left we have to do. We need to apply an initial transformation to the wrapper element via CSS in order to achieve the fancy 3D-effect:

.wrapper {
  transform: rotateX(-90deg) translateY(150px) translateZ(50px);
  ...
}

We can now apply the concepts we learned to build the timeline:

this.timeline
  .add('start')
  .from(this.wrapper.nativeElement, .15, { opacity: 0 }, 'start')
  .to(this.wrapper.nativeElement, .3, { rotationX: 0, y: 0, z: 0,  ease: Power3.easeIn}, 'start')
  .add('image', '-=0.1')
  .add('main', '-=0.15')
  .add('icons', '-=0.1')
  .add('text', '-=0.05')
  .from(this.profileImageBorder.nativeElement, .3, { scale: 0 }, 'image')
  .from(this.profileImage.nativeElement, .3, { scale: 0, delay: .05 }, 'image')
  .from(this.main.nativeElement, .4, { y: '100%' }, 'main')
  .staggerFrom([this.username.nativeElement, this.title.nativeElement], .3, { opacity: 0, left: 50 }, 0.1, 'image')
  .staggerFrom(this.statsIcons, .3, { opacity: 0, top: 10 }, 0.1, 'icons')
  .staggerFrom(this.statsTexts, .3, { opacity: 0 }, 0.1, 'text')
  .play();

Woah! This looks pretty overwhelming at a first glance. But all we do is is to orchestrate our animation using GreenSock’s timeline API. With the help of labels we can run multiple animations in parallel and precisely control the timing of certain animations.

One thing we haven’t talked about so far is .staggerFrom(). A stagger is an animation that contains a delay between each successive animation.

The whole animation can be illustrated as follows:

animation timeline

Here’s the full-fledge solution. Take a look and fiddle with it.

Declarative implementation using Angular Animations

In the previous section we have seen how to implement the profile animation with GreenSock in an imperative way. There are some drawbacks to that solution. First, it’s quite some boilerplate and work to collect all the elements and manuelly set up the timeline. That said, an animation platform like GSAP requires the DOM to be ready. A framework can make much more assumptions about the instructions (animation data) and the environment (app and browser) before animating things. Second, it can be very beneficial if there’s a framework backing the animation engine, like with Angular. GSAP and other animation libraries cannot easily handle DOM insertions or removals because they do not own the DOM transactions. Angular on ther other hand has full control over the DOM.

If you are completely new to animations with Angular, check out this post by Thomas Burleson. It covers the fundamentals and shows an example of a more complex fade animation.

Alright, let’s refactor our profile animation using the latest animation features introduced with Angular 4.2. To get started, we first have to import the BrowserAnimationsModule from @angular/platform-browser/animations and add it to the imports of our application:

@NgModule({
  imports: [
    BrowserAnimationsModule
    ...
  ],
  ...
})
export class DashboardModule {}

Remember, animations in Angular are based on the WAAPI and work in browsers that support it including Chrome and Firefox. However, currently Internet Explorer and Safari do not. In this case a polyfill is required to achieve similar results. Once animations are properly imported and enabled, we can go ahead and start defining the profile animation.

To quickly recap, animations are declared with the animations metdadata property within the @Component() decorator. Each animation is defined by a trigger which takes a name and a list of state and transition entries. Angular’s animation engine works basically like a state machine. That’s right. This should sound familiar. We have seen it in the beginning of this post, remember? The first argument of transition allows you to specify a direction from one state to another, also known as state-change-expression. Here are some common values:

  • * => * captures a state change between any states
  • void => * captures the entering of elements
  • * => void captures the leaving of elements

The last two are so common that they have their own aliases:

  • :enter for void => *
  • :leave for * => void

Angular 4.2 introduces several new animation features and extends Angular’s animation DSL. Here’s a quick overview of what’s new:

  • query() can be used to find one or more elements within the element that’s being animated
  • stagger() animates a bunch of elements with a delay in between each animation
  • group() specifies a list of animations that are run in parallel
  • sequence() specifies a list of animations that are run one at a time
  • animation() can be used to create reusable animations with input parameters for
  • useAnimation() invokes reusable animations created with animation()
  • animateChild() will invoke child animations which are normally blocked

Neat! Let’s use that to re-implement our profile animation with Angular’s animation DSL. In order to demonstrate most of the above animation helpers, espeically animateChild(), we need to refactor our application a bit.

First of all, we create a new component called ProfileStatsComponent which now contains the ul that was perviously part of the DashboardComponent. The template of the DashboardComponent now looks like this:

<div class="wrapper">
  <header>
    <div class="profile-image-wrapper">
      <div class="profile-image-border"></div>
      <img class="profile-image" src="https://api.adorable.io/avatars/90/me@you.com.png" />
    </div>
    <div class="profile-header-content">
      <span class="username">{{ user.name }}</span>
        <span class="username-title">{{ user.title }}</span>
    </div>
  </header>
  <main>
    <profile-stats [user]="user"></profile-stats>
  </main>
</div>

The dasboard now composes the ProfileStatsComponent which will later define its own animation. For now, let’s focus on the profile animation and talk about child animations in a minute.

Here’s how we define our profileAnimation:

animations: [
  trigger('profileAnimation', [
    transition(':enter', group([
      ...
    ]))
  ])
]

Within our profileAnimation we define one transition and on :enter (when the dialog enters the DOM) we run several animations in parallel. Next, we use query() to grab the DOM elements we need for our animation and set some initial styles using the styles helper:

animations: [
  trigger('profileAnimation', [
    transition(':enter', group([
      query('.wrapper', style({ opacity: 0, transform: 'rotateX(-90deg) translateY(150px) translateZ(50px)' })),
      query('.profile-image-border, .profile-image', style({ transform: 'scale(0)' })),
      query('.username, .username-title', style({ opacity: 0, transform: 'translateX(50px)' })),
      query('main', style({ transform: 'translateY(100%)' }))
    ]))
  ])
]

Remember how we collected the DOM elements using @ViewChild() and @ViewChildren()? We don’t need to do that anymore. Plus, we can get rid of all the local template references because that is now handled by query(). Quite powerful, huh?

Before we implement the profile animation, let’s create a reusable fade animation that we can use elsewhere in different places with full input parameter support:

export const fadeAnimation = animation([
  animate('{{ duration }}', style({ opacity: '{{ to }}' }))
], { params: { duration: '1s', to: 1 }});

The fadeAnimation can now be imported into our application, adjusted via input parameter and invoked using useAnimation(). The values we specified for the input parameters are default values.

Once we have that in place, let’s add the missing pieces to our animation:

animations: [
  trigger('profileAnimation', [
    transition(':enter', group([
      // Initial Styles
      ...

      query('.wrapper', group([
        useAnimation(fadeAnimation, {
          params: {
            duration: '150ms',
            to: 1
          }
        }),
        animate('300ms cubic-bezier(0.68, 0, 0.68, 0.19)', style({ transform: 'matrix(1, 0, 0, 1, 0, 0)' }))
      ])),

      query('.profile-image-border', [
        animate('200ms 250ms ease-out', style('*'))
      ]),

      query('.profile-image', [
        animate('200ms 300ms ease-out', style('*'))
      ]),

      query('.username, .username-title', stagger('100ms', [
        animate('200ms 250ms ease-out', style('*'))
      ])),

      query('main', [
        animate('200ms 250ms ease-out', style('*'))
      ])

      ...
    ]))
  ])
]

In the code above, we query a bunch of elements and use several animation helpers to achieve the desired effect. All animations will run in parallel because they are defined within a group(). Also, there are no “labels” or a similar feature to what GreenSock provides with .add(). Turns out, Angular has no timeline support yet and we need to fiddle with delays in order to orchestrate the animation.

If we take a closer look we can see that there’s actually more to it. For instance for the wrapper, we run two animations in parallel one of which is the reusable animation we defined earlier. We can invoke a reusable animation with the useAnimation() method. While AnimationOptions are optional, we specify them to override the default input paramters.

Furthermore, we can spot this special style property of style('*'). This will basically remove all of the special styling we have added (e.g. initial styles) and reset the state of the element. It’s an equivalent of setting each value to *. This means that Angular will figure out the values at runtime. On top of that use the stagger() animation helper method to animate multiple elements with a timing gap in between each animated element.

Applying animations using @HostBinding()

Ok, but how do we use the animation? For that we can either attach the trigger to the element within the component’s template or use a @HostBinding(). In our case, we use the @HostBinding() because we want to attach the trigger to the host element:

export class ProfileStatsComponent {
  ...

  @HostBinding('@profileAnimation')
  public animateProfile = true;

  ...
}

Understanding child animations

In a real-world scenario you most likely end up having multiple components and animations on different levels, e.g. parent or child animations. Turns out that parent animations will always get priority and any child animation will be blocked. That’s a shame. But don’t bury your head in the sand yet because Angular got you covered! We can query inner elements and use animateChild() to allow child animations to run. The cool thing is we can do that at any point in the animation sequence within a defined transition.

In our example, we created a component called ProfileStatsComponent. Let’s see this in action and start off by creating a child animation for this component using everything we know by now:

animations: [
  trigger('statsAnimation', [
    transition('* => *', group([
      query('.stats-icon', style({ opacity: 0, transform: 'scale(0.8) translateY(10px)' })),
      query('.stats-text', style({ opacity: 0 })),

      query('.stats-icon', stagger('100ms', [
        animate('200ms 250ms ease-out', style('*'))
      ])),

      query('.stats-text', stagger('100ms', [
        animate('200ms 250ms ease-out', style('*'))
      ])),
    ])
  ])
]

Easy, right? Now we can go ahead and use the animateChild() helper as mentioned earlier in our profileAnimation:

animations: [
  trigger('profileAnimation', [
    transition(':enter', group([
      // Initial Styles
      ...

      // Animation
      ...
      query('profile-stats', animateChild())
    ]))
  ])
]

That’s it. We fully re-implemented the profile animation using Angular’s built-in animation system. It’s very intuitive, easy to use and declarative.

Here’s the demo. Try it out and fiddle with it!

If you want to find out more about the new animation features in Angular 4.2+, check out this excellent post by Matias Niemela.

Special Thanks

Kudos to Matias Niemelä for the amazing work on the Angular Animation system!

Source:: Thoughtram

Build a Cryptocurrency Comparison Site with Vue.js

By rdegges

Vue.js is a simple Javascript framework that lets you build dynamic front-end web applications. Lots of people compare it to React and Angular.

As a back-end developer, and someone not incredibly experienced with frontend web applications, I’ve found Vue.js a lot simpler to learn, use, and be successful with vs. React and Angular.

In this article, I’ll walk you through the basics of Vue.js, and in the process we’ll build a very simple single page application that compares the prices of the top 10 cryptocurrencies. To keep things simple, we’ll just be using plain old HTML, CSS, and Javascript for this – nothing fancy required.

Who is Vue Built For?

Vue.js is a framework designed to handle the view layer of your web applications. This means Vue will only handle things related to displaying the user interface on the page.

Vue uses plain old HTML, and doesn’t require you to learn another language (like JSX with React), and can therefore be picked up by web designers and other front-end developers who are familiar with HTML already.

If you’re looking for a simple Javascript framework to help you dynamically display data, you can’t really go wrong with Vue:

Vue is simple, fast, and well documented.

Vue Basics

Let’s take a look at a minimal Vue.js web page:

<html>
  <body>
    <!-- All that Vue cares about is what is inside this div. -->
    <div id="app">
    </div>
    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app"
      });
    </script>
  </body>
</html>

This minimal web page will initialize Vue.js fully, and have it manage everything inside of the app div. The el parameter in the Vue constructor tells Vue to bind itself to the element (in this case a div) whose id is app.

When Vue starts up, it will begin scanning and managing all the code inside this div.

Data Management

One of the core things Vue does is manage data. In order to tell Vue what data you want to manage, you need to explicitly declare every bit of data you expect Vue to dynamically manage for you upfront in a special data object, like so:

<html>
  <body>
    <div id="app">
      <h1>{{ message }}</h1>
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app",
        data: {
          message: "Hi!"
        }
      });
    </script>
  </body>
</html>

Hi Screenshot

As you can see in the example above, I’m defining a new data object inside the Vue constructor, and defining my data inside of it. In this case, I’m just telling Vue that I’d like it to manage a message for me.

You’ll also notice that my HTML code now contains a variable: {{ message }}. After Vue starts up, and scans the app div for code, it will start replacing any variables it finds with the related data objects you’ve defined.

If you open the Javascript console in your browser, and attempt to modify the message value like so:

app.message = "yo";

You’ll notice that the contents of the page will change!

Yo Screenshot

This happens because Vue is managing that data for you. When the data changes, Vue re-renders the variable on the page.

This behavior makes it really easy to build dynamic web applications, since any time you change data in Javascript, the page is re-rendered on your behalf and you don’t need to worry about any sort of UI management.

Conditionals

In addition to managing pieces of data, Vue also supports writing programming logic in your HTML templates.

Vue supports typical if, else if, and else conditionals using the v-if, v-else-if, and v-else syntax.

Let’s take a look at a simple Vue application that uses an if-else conditional.

<html>
  <body>
    <div id="app">
      <h1>{{ message }}</h1>
      <p v-if="secretMessage">This is a secret HTML element.</p>
      <p v-else>Welcome to the website.</p>
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app",
        data: {
          message: "Hi!",
          secretMessage: false
        }
      });
    </script>
  </body>
</html>

As you can see above, we now have two paragraph tags, each with a different message. If you run this in your browser, you’ll see the following:

Welcome to the Website Screenshot

Because the secretMessage variable is false, the conditional statement in HTML will fail to execute, and the else statement code will be ran, outputting the HTML element with the welcome message.

Since we know Vue is dynamic, we can now open the Javascript console, modify the value of secretMessage, and BAM, the page will re-render with the secret message being shown.

Secret Message Screenshot

One important thing to note: when using Vue conditionals, the DOM will be modified. In the example above, when we enable the secretMessage flag and show the message, the previously shown paragraph tag will be completely removed from the DOM.

Looping

Vue also supports simple loops. Here’s a small example application that uses Vue to loop through an array of shopping items, displaying them in an ordered list:

<html>
  <body>
    <div id="app">
      <p>Shopping list</p>
      <ol>
        <li v-for="item in shoppingList">{{ item }}</li>
      </ol>
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app",
        data: {
          shoppingList: [
            "milk",
            "eggs",
            "steak",
            "chicken"
          ]
        }
      });
    </script>
  </body>
</html>

When this runs in the browser, you’ll see that the v-for directive Vue provides will repeat itself, looping over the contents of the shoppingList array:

Shopping List Screenshot

If you go modify the shoppingList array in the Javascript console, you’ll notice the same behavior as before: Vue will re-render the shopping list for you automatically.

Two-Way Data Management

The next basic concept we’ll cover is two-way data binding. Up until now, you’ve seen that whatever data you define in Vue will appropriately render when changed. This is really useful, but there’s a little bit more we can do.

In some circumstances, like when accepting user input, you’ll also want Vue to manage user controlled data. This is called two way data binding: when both the user (and you) can manage the same pieces of data in your application.

Let’s take a look at a simple Vue application that uses two-way data binding:

<html>
  <body>
    <div id="app">
      <p>What's your favorite color?</p>
      <input v-model="color" type="text">
      <p>Your favorite color is... {{ color }}</p>
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app",
        data: {
          color: ''
        }
      });
    </script>
  </body>
</html>

As you can see if you try this out yourself, when you enter data into the input box, the color variable is changed, and then re-rendered on the page. This is two-way data binding.

Favorite Color Screenshot

If you go into the Javascript console and modify this directly yourself, you’ll see it change again.

Doing Things with Methods

Now that we’ve covered some of the fundamental Vue.js properties, let’s talk about something a little more interesting: Vue methods.

In addition to managing data, Vue also provides a convenient way to structure your Javascript actions.

Let’s take a look at a very simple example application that uses a method to capitalize the color from the example above when clicked:

<html>
  <body>
    <div id="app">
      <p>What's your favorite color?</p>
      <input v-model="color" type="text">
      <p>Your favorite color is... {{ color }}</p>
      <input type="button" v-on:click="capitalizeColor" value="Capitalize">
    </div>

    <script src="https://unpkg.com/vue"></script>
    <script>
      let app = new Vue({
        el: "#app",
        data: {
          color: ''
        },
        methods: {
          capitalizeColor: function() {
            this.color = this.color.toUpperCase();
          }
        }
      });
    </script>
  </body>
</html>

Capitalize Screenshot

In this example, we define a function in the methods object of Vue, which simply capitalizes the color variable from before. We then use the v-on:click attribute on out input tag to tell Vue that when the button is clicked, it should run that method.

Pretty straight-forward, right?

Build a Cryptocurrency Comparison Website with Vue

Now that we’ve had a chance to look at some basic Vue.js patterns and usage, let’s try to build something with our new found knowledge!

I’ve been using Bitcoin for many years now, and enjoy working with it, so I thought it’d be fun to hack together a little single page app that displays the top 10 cryptocurrencies as well as their price data.

This could be a useful dashboard for someone who’s considering purchasing cryptocurrencies for speculative purposes, as it gives you an idea of how these things are valued at any given time.

So… Let’s get started!

NOTE: If you’d rather just look at the completed project on Github, you can check it out at github.com/rdegges/cryptocompare.

Bootstrapping

The first thing we’ll do is get our page bootstrapped with some basic HTML, Javascript libraries, etc.

Because I’m not a web designer (and have poor visual taste!), we’ll be using Twitter’s Bootstrap library for basic styling. We’ll also be using Vue.js, and a few other small helper libraries which will be explained later on:

  • vue2-filters, a simple Vue.js library that provides some useful template filters for displaying text. In this app, I only the filter for helping to display currency values nicely.
  • axios, a popular Javascript library for making HTTP requests

All in all, our basic HTML page with all our dependencies included will look like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>CryptoCompare</title>

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" href="https://bootswatch.com/simplex/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/style.css">

    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body class="container">
    <h1>Crypto Compare</h1>

    <div class="row">
      <div class="jumbotron col-xs-offset-2 col-xs-8">
        <p>
          This website indexes the top 10 cryptocurrencies by market cap (how
          much the sum of all coins is collectively worth), and gives you an easy
          way to compare cryptocurrency performance and rank over the last week.
        </p>
      </div>
    </div>

    <div id="app">
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/vue"></script>
    <script src="/static/js/vue2-filters.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="/static/js/app.js"></script>
  </body>
</html>

We’ve got all our dependencies included, we’ve got a basic HTML page, and we’re ready to start doing some real development work.

Design Your Page

The first thing I like to do after bootstrapping my page is to lay out the elements where they belong. In this case, since we’ll be building a ranked list of the top cryptocurrencies, we’ll have a very simple design. It’s just an HTML table that lists the top cryptocurrencies, and their associated stats.

Here’s the modified HTML and CSS, and what it looks like after defining these basic elements:

<div id="app">
  <table class="table table-hover">
    <thead>
      <tr>
        <td>Rank</td>
        <td>Name</td>
        <td>Symbol</td>
        <td>Price (USD)</td>
        <td>1H</td>
        <td>1D</td>
        <td>1W</td>
        <td>Market Cap (USD)</td>
    </thead>
    <tbody>
    </tbody>
  </table>
</div>
/* /static/css/style.css */

h1 {
  text-align: center;
}

td img {
  width: 25px;
}

.jumbotron p {
  font-size: 1.2em;
}

.jumbotron {
  margin-top: 5em;
  margin-bottom: 5em;
}

CryptoCompare Basic Website

We’re off to a good start! We’ve now got our basic website layout done, and we have a good idea of what data we’ll need to make this work, etc.

Fetch Cryptocurrency Data

In order to build the rest of this app, we need a way to retrieve a list of the top cryptocurrencies, as well as their stats. For this project, I thought it’d be fun to show the following:

  • The top 10 cryptocurrencies by market cap (the total worth of all coins for the given currency)
  • Each currency’s name
  • Each currency’s symbol (this is how people typically refer to the currencies on trading platforms)
  • The current price of a single coin in each currency, in USD
  • The percent change in value of the currency over the last hour, day, and week

Luckily for us, there’s a great free API service that provides this exact data. It’s called CoinMarketCap. It doesn’t require any registration or setup, you can just read through their API documentation, figure out how the API endpoints work, and plug it into whatever you’re building.

We’ll need to use the ticker API provided by CoinMarketCap, which will allow us to grab a list of the top 10 cryptocurrencies, and display their stats. Here’s what this API call looks like, and what it returns:

[
  {
    "id": "bitcoin",
    "name": "Bitcoin",
    "symbol": "BTC",
    "rank": "1",
    "price_usd": "2747.54",
    "price_btc": "1.0",
    "24h_volume_usd": "2242640000.0",
    "market_cap_usd": "45223373666.0",
    "available_supply": "16459587.0",
    "total_supply": "16459587.0",
    "percent_change_1h": "-2.83",
    "percent_change_24h": "19.78",
    "percent_change_7d": "17.2",
    "last_updated": "1500596647"
  },
  ...
]

As you can see above, the resulting JSON is an array of the top 10 cryptocurrencies. Each JSON object in the array represents one currency, and contains all the information we need.

There is one thing we’re missing though. To make our table look nice, I’d also like to display the logo of each cryptocurrency next to their name.

Unfortunately, the CoinMarketCap API doesn’t have any images, so I did a little Google-ing around and found another API we can use to retrieve metadata about tons of different cryptocurrencies, including their logos!

The CryptoCompare API provides us this data. Much like CoinMarketCap, there’s no registration required.

The API call we’ll use to retrieve data about all the cryptocurrencies looks like this:

$ curl https://www.cryptocompare.com/api/data/coinlist
{
  ...
  "Data": {
    "AVT": {
      "Id": "138642",
      "Url": "/coins/avt/overview",
      "ImageUrl": "/media/1383599/avt.png",
      "Name": "AVT",
      "CoinName": "AventCoin",
      "FullName": "AventCoin (AVT)",
      "Algorithm": "N/A",
      "ProofType": "N/A",
      "FullyPremined": "0",
      "TotalCoinSupply": "10000000",
      "PreMinedValue": "N/A",
      "TotalCoinsFreeFloat": "N/A",
      "SortOrder": "1266"
    },
    ...
  }
}

With the new data we have from the API call above, we can correlate a cryptocurrency’s symbol (for instance BTC, AVI, etc.) with an ImageUrl that shows a picture of that currency.

Using this new data, plus the data from CoinMarketCap, we now have everything we need!

Set Up Your Vue App

Now that we know how to grab the data we need, let’s start building out our Vue app. To keep things simple, all our Vue code will be placed into the file static/js/app.js.

The first thing we need to do here is scaffold our Vue app, and tell it what variables we expect it to manage:

/**
 * Our Vue.js application.
 *
 * This manages the entire front-end website.
 */

// The API we're using for grabbing metadata about each cryptocurrency
// (including logo images). The service can be found at:
// https://www.cryptocompare.com/api/
let CRYPTOCOMPARE_API_URI = "https://www.cryptocompare.com";

// The API we're using for grabbing cryptocurrency prices.  The service can be
// found at: https://coinmarketcap.com/api/
let COINMARKETCAP_API_URI = "https://api.coinmarketcap.com";

// The amount of milliseconds (ms) after which we should update our currency
// charts.
let UPDATE_INTERVAL = 60 * 1000;

let app = new Vue({
  el: "#app",
  data: {
    coins: [],
    coinData: {}
  },
  methods: {

    /**
     * Load up all cryptocurrency data.  This data is used to find what logos
     * each currency has, so we can display things in a friendly way.
     */
    getCoinData: function() {
    },

    /**
     * Get the top 10 cryptocurrencies by value.  This data is refreshed each 5
     * minutes by the backing API service.
     */
    getCoins: function() {
    },

    /**
     * Given a cryptocurrency ticket symbol, return the currency's logo
     * image.
     */
    getCoinImage: function(symbol) {
    }
  }
});

The two variables we’re going to have Vue manage for us to make this possible are:

  • coins – which will be an array of all the different types of cryptocurrencies (bitcoin, ethereum, etc.), and
  • coinData – which will be an object loaded from the CryptoCompare API service we looked at earlier. Using this we’ll be able to cross reference cryptocurrency data to get a logo image.
    We’re also going to define three stub methods that we’ll need to implement:

  • getCoinData – which will retrieve the coin data from CryptoCompare
  • getCoins – which will load the coin data from CoinMarketCap
  • getCoinImage – which takes a currency symbol and returns a link to that currency’s logo image

With these three methods in place, we can now begin writing some software!

Implement Your Coin Methods

Let’s start by implementing the getCoins method that will talk to the CoinMarketCap API. We know from before that all we need to do is hit the API and store the resulting array of JSON data in our Vue variable.

To make API requests, I like to use the axios Javascript library (mentioned earlier on in this article). It’s a very popular library for making HTTP requests in client side Javascript.

Since I’ve already loaded it up for us in the web scaffold, we can use it directly in our Vue code:

getCoins: function() {
  let self = this;

  axios.get(COINMARKETCAP_API_URI + "/v1/ticker/?limit=10")
    .then((resp) => {
      this.coins = resp.data;
    })
    .catch((err) => {
      console.error(err);
    });
},

As you can see, we can easily use axios to issue a GET request for us, grab the resulting array of data, and then store it as self.coins (which updates the Vue coins variable). Once that variable change occurs, Vue will re-render any part of our page that’s dependent upon it.

Next, let’s implement our other method: getCoinData, which retrieves metadata about many different types of cryptocurrencies, and will be used to find their logo images:

getCoinData: function() {
  let self = this;

  axios.get(CRYPTOCOMPARE_API_URI + "/api/data/coinlist")
    .then((resp) => {
      this.coinData = resp.data.Data;
      this.getCoins();
    })
    .catch((err) => {
      this.getCoins();
      console.error(err);
    });
}

This is another simple API call: we make the request and update the data. One thing we will do here, however, is call the getCoins method as well. We do this because we’ll only need to call this method once when the page has loaded (to load up all the currency metadata), but will need to call the getCoins method many times to keep the data up-to-date, and we should only call it once the metadata about all coins has already been loaded.

The last major coin method we need to implement is getCoinImage, which takes in a coin’s symbol and returns the full image URL. Here’s how we do that:

getCoinImage: function(symbol) {
  return CRYPTOCOMPARE_API_URI + this.coinData[symbol].ImageUrl;
}

This code takes care of the cross-referencing between both API services, and allows us to easily retrieve a cryptocurrency’s image.

Load Data When the App Starts

Now that we’ve built some of our data-retrieval methods, we need to actually run them. Otherwise: oup app will open, but nothing will happen.

In Vue, we can bind actions to certain events that happen on the page. One of these lifecycle events is called “created”, and we can use this lifecycle hook to run our code once the Vue app has been fully initialized in the browser.

Here’s what it looks like:

let app = new Vue({
  // ...
  created: function() {
    this.getCoinData();
  }
});

This is exactly what we need, because once the page is loaded, we’ll call our getCoinData method which will retrieve all coin image URls, then we’ll call our getCoins method which will load the initial data we need to eventually display on the page.

Finally, we need to ensure that we actually keep the data on the page updating. The CryptoMarketCap API service says that their data is refreshed once every 5 minutes, so as to not be aggressive, we’ll be querying their API once per minute to display new results to our users.

We can do this easily outside of our Vue app using a plain old call to Javascript’s setInterval function:

/**
 * Once the page has been loaded and all of our app stuff is working, we'll
 * start polling for new cryptocurrency data every minute.
 *
 */
setInterval(() => {
  app.getCoins();
}, UPDATE_INTERVAL);

Notice how we’re able to run our Vue method outside of our Vue app by calling it off the Vue app object. Vue publicly exports all of your data and methods so they can be used outside Vue.

Displaying Your Data

Now that we’ve built the data management side of things, let’s hop back into our HTML code and actually display some of this shiny new data for our users.

Using the same constructs we learned earlier, we’re going to loop through the cryptocurrency data, filling out our table:

<table class="table table-hover">
  <thead>
    <tr>
      <td>Rank</td>
      <td>Name</td>
      <td>Symbol</td>
      <td>Price (USD)</td>
      <td>1H</td>
      <td>1D</td>
      <td>1W</td>
      <td>Market Cap (USD)</td>
  </thead>
  <tbody>
    <tr v-for="coin in coins">
      <td>{{ coin.rank }}</td>
      <td><img v-bind:src="getCoinImage(coin.symbol)"> {{ coin.name }}</td>
      <td>{{ coin.symbol }}</td>
      <td>{{ coin.price_usd | currency }}</td>
      <td>
        <span v-if="coin.percent_change_1h > 0">+</span>{{ coin.percent_change_1h }}%
      </td>
      <td>
        <span v-if="coin.percent_change_24h > 0">+</span>{{ coin.percent_change_24h }}%
      </td>
      <td>
        <span v-if="coin.percent_change_7d > 0">+</span>{{ coin.percent_change_7d }}%
      </td>
      <td>{{ coin.market_cap_usd | currency }}</td>
    </tr>
  </tbody>
</table>

Pretty straightforward, right?

The only new thing here is the v-bind directive. If you’re wondering what that does, it tells Vue to run the getCoinImage function, grab the result, and use that result for the img tag’s src attribute (this is how we’re able to display the logo to the user).

The final thing we’ll want to do now is clean the coloring up a bit:

  • If the percent change in a currency is positive, we should color it green
  • If the percent change in a currency is negative, we should color it red

This adds a bit more visual flair to the page, and makes it a little easier to eyeball the performance of a currency.

So, let’s quickly build a method and plug it into our HTML:

/**
 * Return a CSS color (either red or green) depending on whether or
 * not the value passed in is negative or positive.
 */
getColor: (num) => {
  return num > 0 ? "color:green;" : "color:red;";
}

<tbody>
  <tr v-for="coin in coins">
    <td>{{ coin.rank }}</td>
    <td><img v-bind:src="getCoinImage(coin.symbol)"> {{ coin.name }}</td>
    <td>{{ coin.symbol }}</td>
    <td>{{ coin.price_usd | currency }}</td>
    <td v-bind:style="getColor(coin.percent_change_1h)">
      <span v-if="coin.percent_change_1h > 0">+</span>{{ coin.percent_change_1h }}%
    </td>
    <td v-bind:style="getColor(coin.percent_change_24h)">
      <span v-if="coin.percent_change_24h > 0">+</span>{{ coin.percent_change_24h }}%
    </td>
    <td v-bind:style="getColor(coin.percent_change_7d)">
      <span v-if="coin.percent_change_7d > 0">+</span>{{ coin.percent_change_7d }}%
    </td>
    <td>{{ coin.market_cap_usd | currency }}</td>
  </tr>
</tbody>```

With these final changes, load the code up in your browser, and give it a go!

![CryptoCompare Final](https://cdn.scotch.io/36632/d98FpTy1SsmPwUgCkcyH_cryptocompare-final.png)

If you play around with the website a bit, you'll notice that every 60 seconds the data is updated as designed.

In the event one currency takes over another's ranking, all the items will shift seamlessly on the page (you can play around with this in the Javascript console by modifying `app.coins` directly if you want).

## Put It All Together

I hope you had fun learning a bit about Vue, and seeing how to use it to build basic web apps.

If you're a web developer looking to organize your front-end logic in a simpler and maintainable way, I strongly recommend you give Vue.js a try.

And finally, if you're interested in reading other articles about web development, APIs, and security, you should check out the [Okta DevBlog](https://developer.okta.com/blog/), and follow [@oktadev](https://twitter.com/oktadev) on twitter.

Source:: scotch.io

Node.js Tests: Mocking HTTP Requests

By John Kariuki

Failing test

Writing tests for an application that relies on external services, say, a RESTful API, is challenging. More often than not, an external resource may require authentication, authorization or may have a rate limiting. Hitting an endpoint for a service hosted in a service like AWS as part of testing would also incur extra charges.

This quickly goes out of hand when you are running tests a couple of times a day as a team, as well as part of continous integration. Nock, a HTTP mocking and expectations library for Node.js can be used to avoid this.

Objectives

By the end of this post, we will have achieved the following.

  • Written a simple Node.js application that makes a HTTP request to an external endpoint.
  • Write tests for the application
  • Mock the requests in the test.

Setting Up The Project

To get started, create a simple Node.js application by creating an empty folder and running npm init.

mkdir nock-tests
cd nock-tests
npm init

Installing the packages

Next, we will install the following packages for our application and testing environment.

  • Axios – A Promise based HTTP client for the browser and node.js
  • Mocha – A popular Node.js testing framework.
  • Chai – A BDD / TDD assertion library for Node.js
  • Nock – A HTTP mocking and expectations library for Node.js
npm install --save axios
npm install --save-dev mocha chai nock

Setting up tests

Our tests will live inside a test directory, Go ahead and create a test directory and create our first test.

mkdir test
touch test/index.test.js

Our first test should be pretty straightforward. Assert that true is, well, true.

/test/index.test.js

const expect = require('chai').expect;

describe('First test', () => {
  it('Should assert true to be true', () => {
    expect(true).to.be.true;
  });
}); 

To run our test, we could run the mocha command from our node_modules but that can get annoying. We are instead going to add it as an npm script.

/package.json

{
  "name": "nock-tests",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "node_modules/.bin/mocha"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.16.2"
  },
  "devDependencies": {
    "chai": "^4.0.2",
    "mocha": "^3.4.2",
    "nock": "^9.0.13"
  }
}

At this point, running npm test on your command-line should give you the following result.

$ npm test
> nock-tests@1.0.0 test /Users/username/projects/nock-tests
> mocha
  First test
    ✓ Should assert true to be true
  1 passing (15ms)

We obviously don’t have any tests making requests, or a useful request for that matter, but we will be changing that.

Creating A Node.js App To Test

Let’s go ahead and write a simple function that makes a HTTP request to the Github API to get a user by username. Go ahead and create an index.js file in the root and add the following code.

/index.js

const axios = require('axios');

module.exports = {
  getUser(username) {
    return axios
      .get(`https://api.github.com/users/${username}`)
      .then(res => res.data)
      .catch(error => console.log(error));
  }
};

Testing: The Wrong Way

Our test will assert that the request made returns an object with specific details. Replace the truthy test we created earlier with the following test for our code.

const expect = require('chai').expect;

const getUser = require('../index').getUser;

describe('Get User tests', () => {
  it('Get a user by username', () => {
    return getUser('octocat')
      .then(response => {
        //expect an object back
        expect(typeof response).to.equal('object');

        //Test result of name, company and location for the response
        expect(response.name).to.equal('The Octocat')
        expect(response.company).to.equal('GitHub')
        expect(response.location).to.equal('San Francisco')
      });
  });
});

Let’s break down the test.

  • We import the getUser method from /index.js.
  • We then call the function and assert that we get an object back and that the user’s name, company and location match.

This should pass on running the test by actually making a request to the Github API.
Let’s fix this!

Testing: The Right Way

Nock works by overriding Node’s http.request function. Also, it overrides http.ClientRequest too to cover for modules that use it directly.

With Nock, you can specify the HTTP endpoint to mock as well as the response expected from the request in JSON format. The whole idea behind this is that we are not testing the Github API, we are testing our own application. For this reason, we make the assumption that the Github API’s response is predictable.

To mock the request, we will import nock into our test and add the request and expected response in the beforeEach method.

/test/index.test.js

const expect = require('chai').expect;
const nock = require('nock');

const getUser = require('../index').getUser;
const response = require('./response');

describe('Get User tests', () => {
  beforeEach(() => {
    nock('https://api.github.com')
      .get('/users/octocat')
      .reply(200, response);
  });

  it('Get a user by username', () => {
    return getUser('octocat')
      .then(response => {
        //expect an object back
        expect(typeof response).to.equal('object');

        //Test result of name, company and location for the response
        expect(response.name).to.equal('The Octocat')
        expect(response.company).to.equal('GitHub')
        expect(response.location).to.equal('San Francisco')
      });
  });
});

The expected response is defined as an export in a separate file.

/test/response.js

module.exports = { login: 'octocat',
  id: 583231,
  avatar_url: 'https://avatars0.githubusercontent.com/u/583231?v=3',
  gravatar_id: '',
  url: 'https://api.github.com/users/octocat',
  html_url: 'https://github.com/octocat',
  followers_url: 'https://api.github.com/users/octocat/followers',
  following_url: 'https://api.github.com/users/octocat/following{/other_user}',
  gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
  starred_url: 'https://api.github.com/users/octocat/starred{/owner}{/repo}',
  subscriptions_url: 'https://api.github.com/users/octocat/subscriptions',
  organizations_url: 'https://api.github.com/users/octocat/orgs',
  repos_url: 'https://api.github.com/users/octocat/repos',
  events_url: 'https://api.github.com/users/octocat/events{/privacy}',
  received_events_url: 'https://api.github.com/users/octocat/received_events',
  type: 'User',
  site_admin: false,
  name: 'The Octocat',
  company: 'GitHub',
  blog: 'http://www.github.com/blog',
  location: 'San Francisco',
  email: null,
  hireable: null,
  bio: null,
  public_repos: 7,
  public_gists: 8,
  followers: 1840,
  following: 6,
  created_at: '2011-01-25T18:44:36Z',
  updated_at: '2017-07-06T21:26:58Z' };

To test that this is the actual response expected in the test, try editing one of the fields in the response object and run the test again. The tests should fail.

In my case, I will be changing the name value to Scotch. You should get the error below.

Conclusion

We have only scratched the surface on what you can do with nock. It has a very detailed documentation on how to use it and it is worth exploring. For instance, If you are writing tests that involve error handling, you could mock error responses with an error message.

nock('http://www.google.com')
   .get('/cat-poems')
   .replyWithError('something awful happened');

Happy testing!

Source:: scotch.io