Category: JavaScript

AngularJS 1.5 Input Components

Introduction

The promise of JavaScript component-based architecture frameworks such as AngularJS 1.5, Angular 2, or Aurelia is enabling developers to write less code, both HTML markup, and JavaScript.  The ability to encapsulate HTML and JavaScript into reusable components should simplify the application consuming these components greatly.

Please, review this post and code, and then start your own component library for your applications. There are enough components here to get you started.  I have chosen to make these components granular with a single responsibility, rather that make a single component with many properties to change the behavior. This approach provides clean and readable HTML markup, clearly describing the components role and behavior.

This blog post and GitHub repo are written using ES6. I wrote why I use ES6 JavaScript here. I also leverage ES7 experimental class property features in my applications.

AngularJS 1.5 components are uncomplicated element directives. These components can encapsulate business logic and are testable.

If you have time, compare the amount of JavaScript code is required by Angular 2 components to the AngularJS 1.5 components. You’ll find that AngularJS 1.5 is simpler and currently requires considerably less framework code to register. You’ll also see that I separate the component registration from the component, this removes Angular from the component enabling very fast executing unit tests.  See the Review Presentation repo example.

The below images show the clean and minimal HTML required to produce a typical data form with comprehensive validation and associated messages, including password strength metrics. The “match” directive (see app.module.js) on the second input-password component handles comparing the two passwords. The corresponding error message is displayed below these components. The input control placeholder is included for instructional purposes only and is probably not needed in a simple form like this one.

html

form

References

I did my research before embarking on creating these input components. I’m very grateful to the many community authors for their blog posts and answers on StackOverflow. The below links helped me to understand this problem space and I recommend that you study them:

Creating a custom form field with validation using AngularJS 1.5 and TypeScript

Password Strength Meter

InputBaseComponent

The InputBaseComponent is the base class for the text input components. It exposes properties common to all HTML text input controls.

The InputBaseComponent creates the component label from the name attribute if the label attribute is not supplied. It’s onInit, onChange, and validate methods are invoked by deriving classes.

Normally an AngularJS 1.5 component does not update it’s parent model directly. As explained in the above blog post this component design calls for updating the model instead of using two-way bindings or a one-way binding with a corresponding event.

I agree with this architecture in this specific use case because it mimics the interaction that an input control would have if it was not encapsulated in a separate component. It is assumed that the form these components are hosted on would be adhering to the typical AngularJS component design pattern described in the AngularJS documentation.

By requiring the model in the component registration (see input-components.module.js), the model will be injected and is available to the component controller for reading and writing. When the view needs to be updated, $render is invoked and the below function updates the component value. When the component value is changed, the model value is set in the onChanage method.

COMPONENT REGISTRATION: 
require: {
    model: "ngModel"
}

ONINIT:
this.model.$render = () => {
  this.value = this.model.$viewValue;
}

ONCHANGE:
this.model.$setViewValue(this.value);

This base class handles setting sane values for the maximumLength and for setting  isRequired based on the minimumLength. The isRequired property is used in the HTML to set a required attribute.

The onChange method provides data input length validation by limiting the value length to the maximumLength.  This code is wrapped in try-catch block to provide a seam for logging an obvious attempt to hack your site. Remember, always defend in layers and never trust any data from the internet either on the client or server.

The validate method checks the input for minimum or maximum length validation rule violations. What is nice about this implementation is that the validation messages can easily be customized to provide the user with good information.  In this application, I’ve chosen to display the validation message next to the label.  If you don’t do this, then be sure to include the label in your validation message.

export default class InputBaseComponent {

  label;
  model;
  value;
  name;
  validationError;
  minimumLength;
  maximumLength;
  isRequired = false;

  constructor(StringUtility) {
    this.StringUtility = StringUtility;
    if(this.name && !this.label) {
      this.label = this.StringUtility.parseWords(this.name);
    }
  }

  onInit() {
    this.model.$render = () => {
      this.value = this.model.$viewValue;
    }
    if(!this.maximumLength) {
      this.maximumLength = 200;
    }
    if(this.minimumLength) {
      this.isRequired = true;
    }
  }

  onChange() {
    try {
      if(this.value && this.maximumLength && 
          this.value.length > this.maximumLength) {
        this.value = this.value.substring(0, this.maximumLength);
      }
    } catch(e) {
      this.value = '';
      // log this to your system as a security message
    }
    this.model.$setViewValue(this.value);
  }

  validate() {
    if(this.isRequired && this.minimumLength) {
      if(!this.value || this.value.length < this.minimumLength) {
        this.validationError = `has a minium length of ${this.minimumLength}`;
        return false;
      }
    }
    if(this.value && this.maximumLength && 
        this.value.length > this.maximumLength) {
      this.validationError = `has a maximum length of ${this.maximumLength}`;
      // log this to your system as a security message
      return false;
    }

    this.validationError = '';
    return true;
  }

}

InputBaseComponent.$inject = ['StringUtility'];

InputTextComponent

Now that we have all the hard work done, let’s extend InputBase and call the required methods.  For an input of type text, no additional validation is required.

import InputBase from '../input-base'

export default class InputTextComponent extends InputBase {

  constructor(StringUtility) {
    super(StringUtility);
  }

  $onInit() {
    super.onInit();
    super.validate();
  }

  onChange() {
    super.onChange();
    super.validate();
  }
}

InputTextComponent.$inject = ['StringUtility'];

InputEmailComponent

The InputEmailComponent extends the InputBase and adds email input validation. Notice that if the call to super validate returns false, this validation does not run.  You can change this logic to display all validation errors or display them one at a time as I do.

import InputBase from '../input-base'

export default class InputEmailComponent extends InputBase {

  constructor(StringUtility) {
    super(StringUtility);
  }

  $onInit() {
    super.onInit();
    this.validate();
  }

  onChange() {
    super.onChange();
    this.validate();
  }

  validate(){
    if(super.validate()) {
      if(this.value && 
          !this.StringUtility.isEmailValid(this.value)) {
        this.validationError = ' has invalid format';
        return;
      }
    }
  }
}

InputEmailComponent.$inject = ['StringUtility'];

InputTextComponent Template

This template has four groups:

  • input-group – apply CSS styles for spacing and alignment
  • form – subform container for the component
  • label-area – displays the label and associated validation messages
  • input – the input control

The validation messages are shown when:

  • the form control is invalid
  • and validation error text has a value
  • and
    • the control has been touched
    • or parent form has been submitted

You can choose when users see validation messages. I’ve used the above logic in my applications and users are happy with the experience.

<input-group>
  <ng-form name="{{vm.name}}Form">
    <label-area>
      <label for="{{vm.name}}">{{vm.label}}</label>
      <error-message ng-show="{{vm.name}}Form.{{vm.name}}.$invalid && 
        vm.validationError && 
        ({{vm.name}}Form.{{vm.name}}.$touched || 
        {{vm.name}}Form.$$parentForm.$submitted)">
          {{vm.validationError}}</error-message>
    </label-area>
    <input
      type="text"
      placeholder="{{vm.label | lowercase}}"
      autocomplete="off"
      ng-model-options="{allowInvalid: true, debounce: 250}"
      name="{{vm.name}}"
      id="{{vm.name}}"
      ng-model="vm.value"
      ng-change="vm.onChange()"
      ng-required="vm.isRequired"
      ng-minlength="vm.minimumLength"
      ng-maxlength="vm.maximumLength"
      maxlength="{{vm.maximumLength}}"
      >
    </input>
  </ng-form>
</input-group>

HTML Input Control

  • placeholder – optional.  I like, that I can bind the label and then use an Angular filter to change the label to lower case.
  • autocomplete – choose the option that meets your requirements
  • ng-model-options
    • allowInvalid – when true, allow ng-change to be raised on keystrokes when the control is invalid.
    • debounce – the time delay for model updating since the last keyboard input.
  • ng-model – binds to the component value
  • ng-change invokes the onChange method
  • ng-required – when true, sets the required attribute on the control
  • ng-minlength – sets the minimum length rule for the control
  • ng-maxlength – sets the maximum length rule for the control
  • maxlength – I STRONGLY urge you to ALWAYS set this attribute on any text input control.  Setting this attribute limits the number of characters that can be entered, pasted or dragged into the control at runtime. Remember that security is always in layers.

Form Submit Button Options

There are two schools of thought around form submit buttons.  One is to disable the submit button when the form is invalid, the other to allow the user to click the submit button and then show all input validation messages.

Your choice will depend on the application requirements and stakeholder choices.

The submit button strategy needs to take into account the applications timing of when to render validation messages.

I prefer to only show validation messages after the user visits the control or when the user submits the form and the control is invalid.

Thoughts

Whether you’re using AngularJS 1.5, Angular 2, Aurelia, et al., components provide the HTML and JavaScript developer a powerful programming paradigm that embraces separation of concerns, test ability, maintainability, readability, and are down right fun to work with.

Download

https://github.com/Oceanware/InputComponents

Close

Have a great day,

Just a grain of sand on the world’s beaches

Component Generator for AngularJS, Angular 2, Aurelia​

Introduction

I believe developers should own their code generation story. The value in owning your code generation is that when platforms change, APIs change, language grammar is enhanced, you can easily refactor your templates and not miss a beat. I also believe that owning your code generation story is a forcing function for thinking out how your application works, is wired up, and how to unit test it.

This tool provides templates that you must edit before generating any code.

What?  No ready-made templates?  Karl, are you nuts?  Why?

Let’s think about code generation templates for a minute. Templates are used to create language specific output.  Developers are using many flavors of JavaScript today: ES5, ES6, ES6 with ES7 experimental features, TypeScript, Coffee Script, etc. Stylesheet files can be written using LESS, SASS, SCSS, or CSS.

What language should I use to write the templates?  I use ES6, but not everyone does.

Let’s think about how Angular apps are structured and wired up.  Check ten blog posts, and you’ll read about ten valid ways to structure and wire up an Angular SPA app.

Small apps are wired up differently than medium or large apps.  Some put small modules in a single folder, whereas a medium-sized module may be within a single parent folder, with each component in a separate folder.

Developers doing unit testing will structure their component wire up differently to better support testing scenarios without having to load Angular for a component or controller unit test.  Not loading Angular for each unit test significantly speeds up your unit tests.

Based on the above language, structure, and wire up options, providing default templates would provide zero value.

Your Small Part

You’ll edit the empty template files for the component based SPA frameworks you author applications for such as AngularJS, Angular 2, or Aurelia. Having you edit your templates provides the best solution for this tool supporting many different: JavaScript flavors, AngularJS coding styles, and component wiring up strategies. Additionally, you’ll be the proud owner of your code generation story.

This tool uses Underscorejs templates that are easy to author, usually requiring a minute or two. However, if your scenario requires it, you can swap out the template engine.

More Than a Tool

My first version of this tool was written in less than two hours as single ES6 file and worked great. It didn’t have any tests but worked perfectly for my one scenario which was, Angular 1.5.9 components and ES6.

Then my mentoring and scope creep genes kicked into high gear, and I spent a few days and several versions to produce this tool.

I didn’t want to miss an opportunity to share a tool that I think will benefit many developers across languages, SPA frameworks, and scenarios. But to accomplish this goal, the tool would require 100% test coverage, work for any JavaScript language and any SPA component based framework.

I hope that others can learn from the architecture, code, and unit tests presented here. I welcome both positive and negative feedback to improve the tool and code.

This tool was also a forcing function for me to dive deep into authoring testable Node.js tools using ES6. It took me a little time, learning the testing tools, but when I refactored the code, having 100% test coverage paid off in spades.

Background

During my career as a Software Engineer and Architect, I’ve always written tools to increase my productivity and accuracy. Tools like XAML Power Toys or this tool came about because I found myself repeating the same task over again, and knew the computer is capable of making me infinitely more productive.

Two weeks ago I started writing an AngularJS 1.5.9 component based application.  AngularJS components are similar in concept to Angular 2 and Aurelia components although the syntax and implementation are a little different.

After creating my second component, I stopped to write this tool. I’m not going to perform mundane, repetitive tasks like creating: folders, components, controllers, templates, tests, and CSS files; this is a perfect assignment for a tool.

In addition to creating folders and files, the tool leverages user editable templates for generating code that matches your requirements and coding style. The ability to generate boiler maker code yields a significant increase in productivity.

What You’ll Learn

  • Features of the tool
  • Tool Architecture
  • Requirements
  • Installation
  • Local development installation
  • Local production installation
  • Command line usage
  • Editing Templates

Videos

  • Getting Started
  • Modifying the tool
  • Unit testing the tool

Features

  • Node.js command line utility, written using ES6 and TDD methodology
  • Cross-platform Windows, Mac, and Linux (thanks to Node.js)
  • User editable templates drive the generated code
  • Create components for any JavaScript framework like AngularJS, Angular 2, and Aurelia
  • Separating templates by framework enables supporting multiple frameworks
  • Component output folder is optionally created based on a command line option
  • Component controller file is optionally generated based on a command line option
  • Component unit test file is optionally generated based on a command line option
  • Component CSS file is optionally generated based on a command line option
  • You can modify anything about this tool, make it fit like a glove for your workflow

Tool Architecture

This object-oriented application tool was written using ES6. I have separated the functionality of the application into small single-responsibility classes. Besides good application design, it makes understanding and unit testing much easier. When I think of this tool, a .NET Console application immediately comes to mind.

I wrote Guard clauses for every constructor and method. I always add guard clauses, even on private methods, irrespective of language because I am a defensive software engineer. Writing guard clauses requires no extra effort given tools like Resharper and the ubiquitous code snippet feature in most editors and IDEs. Guard clauses future proof code in maintenance mode when another developer makes a false assumption while editing, next think you know a customer is filing a bug.

In some of the classes, I have refactored small sections of code into a method. Doing this makes the code easier to read, comprehend, and simplifies unit testing.

Classes

  • ApplicationError – deriving from Error, an instance of this class is thrown when a user error caused by improper command line usage occurs. It permits the top-level try catch block in the ComponentCreator to perform custom handling of the error.
  • CommandLineArgsUtility – provides functions related to command line arguments.
  • ComponentCreator – performs validation and writes the templates.
  • IoUtility – wrapper around Node.js fs.
  • Logger – wrapper around Console
  • TemplateItem – wrapper around a supported framework, exposes all data as immutable
  • Templates – stores collection of TemplateItems, provides immutable data about code generation templates.

The entry point for the tool is index.js.  This module instantiates a TemplateItem for each supported framework, creates all of the required dependencies for the ComponentCreator and injects them, then invokes the create method on the ComponentCreator instance.

The reason for creating and injecting all external dependencies in the root of the application is to enable unit testing. Dependencies injected using constructor injection can easily be stubbed or mocked.

I have successfully used this type of architecture for writing other complicated Node.js applications.

Dependencies

  • Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
  • Chai – is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.
  • Istanbul – full featured code coverage tool for Javascript
  • Mocha – is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun.
  • Sinon.js – Standalone test spies, stubs, and mocks for JavaScript.
    No dependencies and works with any unit testing framework.
  • Sinon-Chai – provides a set of custom assertions for using the Sinon.JS spy, stub, and mocking framework with the Chai assertion library. You get all the benefits of Chai with all the powerful tools of Sinon.JS.

Requirements

  • Node.js  (install either LTS or Current versions.  Personally I use the Current version.)

On Mac’s I don’t recommend installing Node.js from the Node.js website. If you do, upgrading is a PIA.

For Mac users, use brew. After installing brew, run this command from a terminal:

brew install node

If you use brew, future upgrading or uninstalling Node.js on your Mac is a breeze.

Recommendations

To help understand how this application is setup and how unit testing is setup and invoked, please read my blog post: https://oceanware.wordpress.com/2016/08/10/easy-tdd-setup-for-nodejs-es6-mocha-chai-istanbul/

Installation

Ensure you have installed above requirements.

Download or clone the Component Generator repository here.

Open a terminal or command window, and then navigate to the root folder of the tool and run this command.

npm install

Next, run the unit tests and see the coverage report by executing this command:

npm test

Running the unit test also runs the Istanbul code coverage tool which outputs a detailed coverage report in the /coverage/lcov-report/index.html file.

Local Development Installation

During development, testing, and editing of your Node.js command line tools you’ll need to set up a symbolic link so you can execute your tool from any folder. Setting up a symbolic link instead of installing your Node.js package globally, allows you to continue editing the tool’s code, while at the same time, being able to execute the tool from any folder on your computer.

Mac and Linux

From the root folder of the tool, open a terminal or command window and run this command:

npm link

To test you development installation run this command:

gencomponent

The tool will display the command line usage.

Navigate to another folder and rerun the gencomponent command.  You should get the same output.

Windows

You MUST give yourself Full Permissions on the /node_modules folder before proceeding.

Follow the above steps for Mac and Linux.

Local Production Installation

This step is optional.  If you like the convenience of being able to edit your templates or change the tools code, while at the same time being able to invoke the tool from any folder then, by all means, skip this step.

If you want to install the tool globally, or want to install the tool on other machines, then follow these steps.

Before proceeding, you need to remove the symbolic link you created in the above steps.

Navigate to the root folder of the tool, open a terminal or command window and run the following command:

npm unlink
Window, Mac, and Linux

Navigate to the root folder of the tool, open a terminal or command window and run the following command:

npm install -g

You can now invoke the tool from anywhere on your machine.

After installation, if you need to modify the tool or template, uninstall the tool globally, make the changes and reinstall it globally.

Command Line Usage

Before proceeding, ensure you have created a symbolic link for the tool, or installed it globally.

gencomponent (component name) [-(framework)] [--ftsc]

The component name is required to generate a component.

The framework is optional and defaults to ‘ng’ if not supplied. Default framework can be changed by modifying the code.  Default valid options:

  • -ng
  • -ng2
  • -aurelia

Code generation options are prefaced with a double dash  (–), are optional, and can be in any order. The valid options are:

  • f = create a component folder
  • t = create a component unit test file and controller unit test file for the optional controller
  • s = create a component CSS file
  • c  = create a component controller file

If invalid arguments are provided or you attempt to create a component that already exists, an error message will be displayed at the command line.

Usage Examples

1. Show the command line usage message.

gencomponent

gencomponent ?

2. Create the Sales component in the current folder along with the sales.component js, and sales.template.html files. Templates are selected from the default /templates/ng folder.

gencomponent Sales

3. Create the Sales component in a new component folder named Sales along with the sales.component js, sales.controller.js, sales.template.html, sales.component.spec.js, sales.controller.spec.js, and sales.template.css files.  Templates are selected from the default /templates/ng folder.

gencomponent Sales --ftcs

4. Create the Sales component in the current folder along with the sales.component js, sales.component.spec.js, and sales.template.html files. The templates are selected from the /templates/aurelia folder.

gencomponent Sales -aurelia --t

5. Create the SalesDetail component in a new component folder named SalesDetail along with the salesDetail.component.js, salesDetail.component.spec.js, salesDetail.controller.js, salesDetail.controller.spec.js, and salesDetail.template.html files. The templates are selected from the /templates/ng2 folder.

gencomponent SalesDetail -ng2 --ftc

You can easily modify this tool to handle more or less supported frameworks and change the file naming conventions.

Editing Templates

Please read Underscorejs template documentation, it’s very short.

Template engines allow the template author to pass a data object to methods that resolve the template and produce the generated code.

This tool passes a rich data object that you can use inside your templates.

gencomponent SalesDetail

The below template data object was hydrated and passed to the template engine when the tool was invoked with SalesDetail as the desired component.

{ componentName: 'SalesDetail',
 componentSelector: 'sales-detail',
 componentImportName: 'salesDetail.component',
 controllerImportName: 'salesDetail.controller',
 componentImportNameEnding: '.component',
 controllerImportNameEnding: '.controller',
 templateFileNameEnding: '.component.html',
 componentFileNameEnding: '.component.js',
 controllerFileNameEnding: '.controller.js',
 componentSpecFileNameEnding: '.component.spec.js',
 templateCssFileNameEnding: '.component.css' }

This snippet from the below HTML file shows the syntax for injecting the componentName property value into the generated code.

<%= componentName %>

The below HTML can be found in the /temples/ng/componentTemplate.html file. This demonstrates consuming a data object property in a template.

<h2>Component Generator Data Object</h2>
<p>These are the data properties available in all template files.</p>
<p></p>
<p>componentName = <strong><%= componentName %></strong></p>
<p>componentSelector = <strong><%= componentSelector %></strong></p>
<p>componentImportName = <strong><%= componentImportName %></strong></p>
<p>controllerImportName = <strong><%= controllerImportName %></strong></p>
<p>componentImportNameEnding = <strong><%= componentImportNameEnding %></strong></p>
<p>controllerImportNameEnding = <strong><%= controllerImportNameEnding %></strong></p>
<p>templateFileNameEnding = <strong><%= templateFileNameEnding %></strong></p>
<p>componentFileNameEnding = <strong><%= componentFileNameEnding %></strong></p>
<p>controllerFileNameEnding = <strong><%= controllerFileNameEnding %></strong></p>
<p>componentSpecFileNameEnding = <strong><%= componentSpecFileNameEnding %></strong></p>
<p>templateCssFileNameEnding = <strong><%= templateCssFileNameEnding %></strong></p>

Template Folders

  • /ng – AngularJS
  • /ng2 – Angular 2
  • /aurelia – Aurelia

Available Templates

  • Component – always generated
  • Component Template – always generated
  • Controller – optionally generated
  • Component Unit Test – optionally generated
  • Controller Unit Test – automatically generated if the Controller and Component Unit Test is generated
  • Component Template Stylesheet – optionally generated

Workflow for Editing Templates

Before diving into template editing, you need to know exactly what the outputted generated code needs to look like.

You’ll need to decide on your application structure and wiring up.  Will you put your controllers inside the component file or keep them in separate files? Are you going to write unit tests?

I recommend, writing several components, w0rk out the details, identifying repeated code such as imports or require statements, and commonly used constructor injected objects.

In the below example, I have an existing About controller that represents how I would like my controllers to be generated.

Copy the code to generate into the appropriate template file, and then replace the non-repeating code with resolved values from the template data object.

In the below example, I copied the About controller into the componentTemplate.controller.js file and then replaced the “About” name with the componentName data object property.

class AboutController {
   constructor() {
   }
}

export default AboutController;

This below template will generate the above code.

class <%= componentName %>Controller {
    constructor() {
    }
}

export default <%= componentName %>Controller;

Now repeat the above steps for each template and for each framework you’ll be performing code generation.

Note that some templates will be empty, this is normal for .css and possibly .html files. But at least you didn’t have to waste precious time creating the file.

Videos

Getting Started

This 8-minute video explains how to get started with this tool.

Modifying the Tool

This 11-minute video explains how to modify:

  • templates
  • frameworks
  • file naming conventions
  • template engine

Unit Testing the Tool

This 23-minute video explains unit testing this tool.

Close

Writing your own cross-platform, command-line tools using Node.js is fun.

Having 100% test coverage is not easy and takes time. Just know that your customers and fellow developers will appreciate you putting the effort into a release with 100% test coverage.

Have a great day.

Just a grain of sand on the worlds beaches.

 

Easy TDD Setup for Nodejs ES6 Mocha Chai Istanbul

Introduction

I’m working on a command line tool for AngularJS, Angular2, and Aurelia that creates components from user templates.  It creates the folder, component js file, component template HTML file, optional component template CSS file, and the component spec js file.

The tool generates the code using the underscorejs template engine.  It’s amazing how much code you’ll no longer have to type; boiler maker component wiring up and default unit tests for most components.

As I was writing the tool, I decided to break out the project setup into this small blog post to make the tool blog post simpler and focused. You can use this simple project as a starter or learning tool for your Nodejs ES6 projects.

I wrote this application and the command line tool using the Atom Editor.  I’ve include my Atom Snippets down below that give me a big productivity boost when writing unit tests.

This blog post is much more about setting up a Nodejs project that uses ES6, Mocha, Chai, and Istanbul than how to use these tools. Please refer to the many outstanding blog posts, courses, and tutorials on these tools and ES6.

My Approach To Nodejs ES6

It’s amazing what you can write using Nodejs.  I’ve written complex, multi-process apps that have IoT connected over MQTT and real-time communication to web clients.  Also written simple apps like the above command line tool. Nodejs is wonderful and is what enables Electron to be the prodigious cross-platform desktop application tool that it is.

ES6 is a clean modern language, is simple, familiar looking, and is fun.  I’ve used ES5 and TypeScript  for many projects but settled on ES6. I blogged about my decision here

Using ES6 with Nodejs does not require Babel for your code or unit tests.  I’m not using ES7 features such as class properties or decorators, but I can live with that for now.

I structure my Nodejs apps, perhaps differently than you’ve seen on other blog posts.  Not implying better, just different.

I prefer to write my ES6, Nodejs code like I would any object orientated app, small classes with discrete functionality. In architecture speak, SOLID, DRY, etc.

I also structure my ES6 so that it can be tested.  Sometimes that requires a little rethinking and possibly some refactoring, but it’s worth it.

Hello World

It would be madness to not write the ubiquitous “Hello World” app for my Nodejs demo, so here we go.

When this app is executed, index.js is the entry point, it creates an instance of HelloWorld and invokes the run method. 

Notice that I’m passing the command line arguments into the constructor. I do this to make testing the HelloWorld class much easier than if I didn’t.

index.js

'use strict'

const HelloWorld = require('./app/helloworld');

let c = new HelloWorld(process.argv.slice(2));
c.run();

 

HelloWorld is simple.  If no command line args are passed, the run method will log the greeting.  If args are passed, they will be concatenated and then logged.

helloWorld.js

'use strict'

const Logger = require('./logger');

class HelloWorld {

    constructor (commandLineArgs) {
        this.commandLineArgs = commandLineArgs;
        this.greeting = 'Hello World';
        this.logger = new Logger();
    }

    run() {
        if (this.commandLineArgs && this.commandLineArgs.length) {
            this.logger.log(this.commandLineArgs.join(' '));
        } else {
            this.logger.log(this.greeting);
        }
    }
}

module.exports = HelloWorld;

 

Logger outputs message to the console. I always create a logger for my Nodejs apps, so other classes don’t need console.log, etc.  I like the object oriented approached to keep my code clean and familiar. This is a very simple Logger class, enhance it as required for your apps.

logger.js

'use strict'

class Logger {

    log(message) {
        console.log(message);
    }
}

module.exports = Logger;

Unit Testing Setup

For my Nodejs projects I use the following testing tools:

  • Mocha – unit test framework
  • Chai – BDD / TDD assertion library
  • Istanbul – code coverage tool
  • Sinon – standalone test spies, stub, and mock framework
  • Sinon Chai – extends Chai with assertions for Sinon.

You can use Mocha by itself or Mocha and Istanbul to get coverage.  I like the features of Chai, but at the end of the day, it’s personal preference for testing style.  “Actually testing is critical, test style is not.”

I install the test tools locally in my Nodejs projects rather than globally so that I can have multiple versions of a tool if required. Local installs make the command line longer, but that’s not an issue since the command will be in package.json or in a gulp task, bottom line, you don’t have to type it.

Local install example:  npm install mocha –save-dev

Understanding Mocha Setup and Startup

Node and npm commands are executed from your root folder.

When Mocha is invoked, by default it will look in the /test folder for the mocha.opts file, which is the Mocha configuration file. 

Screen Shot 2016-08-11 at 12.25.45 AM

mocha.opts

The first line tells Mocha which folder to look into for the tests, if not supplied, it will use the /test folder.  I’ve chosen the /app folder because I like to have my unit tests in the same folder as the JavaScript being tested.

The second line loads up the common.js file

The third line tells Mocha to look not only in the app folder but also all sub-folders.

Finally, the fourth line, tell Mocha to quit processing when a test fails. 

Note:  When running your full test suite, or when running on a CI server, the bail option is probably not appropriate.

app
--require ./test/common.js
--recursive
--bail

 

common.js

This setup is optional, but its value is that I don’t have to repeat these require statements for Chai and Sinon in every test js file.

'use strict';

global.chai = require('chai');
global.chai.should();

global.expect = global.chai.expect;
global.sinon = require('sinon');

global.sinonChai = require('sinon-chai');
global.chai.use(global.sinonChai);

 

package.json scripts section

The scripts section of the package.json file makes it easy to run commands, especially commands with long text.

To run a command from the terminal or command line type:  npm run {script command name}

For example,  npm run example or npm run tdd:mac

The example and example2 command run the app with and without command line arguments.

The test command runs all the unit tests and code coverage report.

The tdd:mac command runs Mocha and all your tests.  Then it begins to watch the target folder for any changes.  When a file changes, its reruns the tests automatically.

Note:  mocha -w on Windows does not work, hence the command tdd:mac.  Bugs have been logged.  For now, if you’re on Windows, I recommend writing a glup task that watches the folder and then runs mocha without the -w option.  Optionally, if you’re a WebStorm user, you can set this up in the WebStorm IDE if desired.

My typical workflow on my Mac is to open Atom or WebStorm, view my spec file and code being tested in split view, then in a terminal window I run, npm run tdd:mac and I’m good to go.  I get instant feedback from my Mocha test runner as a write tests or code.

  "scripts": {
    "example": "node index.js",
    "example2": "node index.js Hey world!",
    "test": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha",
    "tdd:mac": "./node_modules/.bin/mocha -w"
  },

logger.spec.js

This unit test verifies that the Logger class will invoke console.log and pass the correct message to it.

When you’re unit tests actually write to the console, the text will be outputted into your Mocha report stream outputted to the console.  To limit the noise, I’ve created the below TestString that blends in nicely with the Mocha report.

The variable ‘sut’ is an acronym for ‘system under test.’  I use ‘sut’ to make it easy for the next person reading my tests to quickly see what object is being tested. Consistent code is much easy to read and maintain.

The Sinon library makes it easy to test class dependencies by either spying, stubbing, or mocking the class or methods.  The reason I don’t use a stub or mock here for console.log is because it will block the Mocha report from being displayed.  The spy was a good fit and the TestString gave me the output I wanted.

'use strict'

const Logger = require('./logger');
const TestString = '    ✓';  // nice hack to keep the mocha report clean. LOL.

describe('Logger', () => {
    it('should log a message to the console', () => {
        let sut = new Logger();
        let spy = sinon.spy(console, 'log');

        sut.log(TestString);

        expect(spy.calledOnce);
        expect(spy.calledWithMatch(TestString));

        spy.restore();
    });
});

 

helloWorld.spec.js

To limit bugs and typo’s I use constants for my expected results and method arguments.

In this simple app, the Logger is exposed as a property on HelloWorld, making it accessible for stubbing at test time.  In a larger app, the Logger would be an injected dependency.  Injected dependencies are a no brainer to stub and mock.

'use strict'

const HelloWorld = require('./helloWorld');
const Logger = require('./logger');
const DefaultGreeting = 'Hello World';
const Arg1 = 'Hello';
const Arg2 = 'there!'

describe('HelloWorld', () => {

    describe('Constructor', () => {

        it('should be created with three properties: commandLineArgs, greeting, and logger', () => {
            let sut = new HelloWorld();
            expect(sut).to.have.property('commandLineArgs');
            expect(sut).to.have.property('greeting');
            expect(sut).to.have.property('logger');
        });

        it('should have default greeting', () => {
            let sut = new HelloWorld();
            expect(sut.greeting).to.equal(DefaultGreeting);
        });

        it('should have command line args set when supplied', () => {
            let sut = new HelloWorld([Arg1, Arg2]);
            expect(sut.commandLineArgs).to.have.lengthOf(2);
            expect(sut.commandLineArgs[0]).to.equal(Arg1);
            expect(sut.commandLineArgs[1]).to.equal(Arg2);
        });
    });

    describe('Run', () => {
        it('should log command line args when supplied', () => {
            let logger = new Logger();
            let stub = sinon.stub(logger, 'log').returns();
            let sut = new HelloWorld([Arg1, Arg2]);
            sut.logger = logger;

            sut.run();

            expect(logger.log).to.have.been.calledOnce;
            expect(logger.log).to.have.been.calledWith(`${Arg1} ${Arg2}`);

            stub.restore();
        });

        it('should log default greeting when no command line args are passed', () => {
            let logger = new Logger();
            let stub = sinon.stub(logger, 'log').returns();
            let sut = new HelloWorld();
            sut.logger = logger;

            sut.run();

            expect(logger.log).to.have.been.calledOnce;
            expect(logger.log).to.have.been.calledWith(DefaultGreeting);

            stub.restore();
        });
    });

});

 

Test Results

Executing npm test or npm run test, produces the following output.

The describe and it blocks are nicely nested in this Istanbul coverage report.

The first item in the Logger group is a black check mark, this is my little hack I mentioned above in logger.spec.js file test.

Screen Shot 2016-08-13 at 2.24.51 PM

Atom Snippets

Atom editor snippets rock.  The very best snippet documentation I’ve read is here, read it and you’ll be a happy camper.

These snippets assist my coding of classes and unit tests.

'.source.js':
  'Fat Arrow':
    'prefix': 'fat'
    'body': '() => {}'
  'describe unit test':
    'prefix': 'dsc'
    'body': """
        describe('$1', () => {
            $2
        });

    """
  'it unit test':
    'prefix': 'itt'
    'body': """
        it('should $1', () => {
            $2
        });

    """
  'Class with Constructor':
    'prefix': 'cctor'
    'body': """
        'use strict'

        class $1 {

            constructor () {
                $2
            }
        }

        module.exports = $1;
    """
  'Method Maker':
    'prefix': 'mm'
    'body': """
        $1($2) {
            $3
        }

    """

Download

https://github.com/Oceanware/nodejs-es6-mocha

Close

I hope this information helps you in setting up a Nodejs project that uses ES6, Mocha, Chai, and Istanbul.

Just a grain of sand on the worlds beaches.

ES2015 (ES6) or Typescript

Introduction

I get the question,  “Karl, why do you use ES2015 (ES6)?”

The answer I give depends on the context of the question, in other words what is the scenario we are asking about.

I will answer the question for each of these scenarios:

  • Authoring JavaScript Framework
  • Authoring Large Line of Business Application with more than a few developers
  • Authoring a small application with one or a few developers

Authoring JavaScript Framework

Without equivocation I would use Typescript for a JavaScript Framework.

Why, because I can transpile to ES2015 or ES5, so I can deliver my framework in Typescript, ES2015, or ES5.

Several years from now, I’ll be able to transpile my framework to ES vNext (as long as Typescript is still around and maintained properly), effectively future proofing my code.

I don’t have the hassle of 3rd party .d.ts files that are old or incomplete because my framework probably does not have many 3rd party dependencies.

If my framework does have them, I have the resources to create the required .d.ts files.  I’ll pay this tax because the benefits outweigh the .d.ts file hassles.

Authoring Large Line of Business Application with More Than a Few Developers

Without equivocation I would use Typescript for building a large line of business application with more than a few developers.

Why, because I can leverage the compile time checking, strong typing, and interfaces that Typescript offers; additionally I would use a linter with very strict rules.

I say this for several reasons.  First, because in a large team project like this, you need to reign in some developers so that they don’t get off the path of sensible and maintainable Typescript (JavaScript).  I care much more about creating a maintainable product than I do about someone’s feelings or creative coding desires.  The very strict linter rules also help developers sharpen their JavaScript coding skills.

Second, because Typescript does perform strong type checking at compile time.

Back all this up with unit and integration tests, and you have the basis for a very successful large line of business application.

Authoring a Small Application with One or a Few Developers

Here is where my answer to the original question changes from Typescript to ES2015.

For all of my personnel projects and blog post projects, I’ll use ES2015 (ES6).

For small team projects, I would still like to use ES2015.

Why?

  • Because I write simple ES2015 JavaScript that looks like C#
  • Because I write very clean ES2015 that is very easy to read
  • Because I use a ES6 linter with very strict rules, helps keep my ES6 clean and I’ve learned a lot from the linter rules I violated
  • Because I don’t want to pay the 15% tax for authoring Typescript (adding the type definitions to the code,  getting the .d.ts files downloaded, and imported in the code.  This 15% does not count towards missing or incorrect .d.ts files.)
  • Because I don’t want to deal with 3rd party .d.ts files that are either out of date or missing – this can be a real bummer
  • Because I like the dynamic nature of JavaScript and leverage that capability on occasion
  • Because for a long time, basically a single developer was managing the Definitely Typed github repro.  I look at it yesterday and it seems to have gotten a face lift and many new developers helping out.
  • Because the tool Microsoft ships for creating .d.ts files does not render a .d.ts file that can be used, I always found myself having to add more code to them to get them to work.
  • Just because you’re using a framework that was authored in Typescript, it does not mean you have to use Typescript.

Obviously, these are my opinions, and I know that others can easily come back with solutions or comments, but after many projects using Typescript this what I’ve decided to do.

I don’t want to give the impression that there is a huge gap between perfect .d.ts files and the few that I had trouble with.  But those few I needed, well, I needed them.  It got old dealing with this problem.  Remember, demo ware does not have this problem.  Its when you’re developing real applications that need libraries for services and features, and those services and features have missing, or outdated .d.ts files.  This is where the bummer begins.  I think if Microsoft delivered a tool that I could point to a JavaScript library and it would render a .d.ts file that could be used in the project, I may back off on this gripe.  But I have tried to make the missing .d.ts files and spent precious time messing with this.

All developers need to evaluate languages, tools, frameworks, and 3rd party dependencies for all of their projects, and pick the ones that meet the needs of that specific project.

Select the best tool for the job, not because the framework was written in, or because it’s new and shiny, or because other developers use it, select a tool because it is the best fit for the given requirements.

Close

So if you ask me if I use Typescript or ES2015, my first question will be, what is the scenario or use case, then I can answer based on the above criteria.

Hope this helps someone and have a great day.

Just a grain of sand on the worlds beaches.