Aurelia

Aurelia's New Validation and Testing Capabilities

Introduction

Aurelia

Aurelia


Aurelia's New Validation and Testing Capabilities

Posted by Aurelia on .
Featured

Aurelia's New Validation and Testing Capabilities

Posted by Aurelia on .

Today we've got the initial releases of our new validation overhaul as well as the first release of some new testing capabilities. As usual, we've got a bunch of additional bug fixes and enhancements to performance as well. Read on for the details.

A New Validation Library

As mentioned above, we've been working on a new approach to validation for Aurelia. This approach would allow any validation library to be used and combined with any CSS framework. It simplifies validation mechanics while opening up tremendous flexibiity. To tell you a bit about it, I'll let core team member Patrick Walters take it from here.


I've been contributing for a while now to some of the core Aurelia plugin libraries. With our Validation library, we realized there were some changes that needed to be made. Unfortunately, fixing things required a re-write but it also provided an opportunity to really refine the concepts and implementations.

I worked heavily with Jeremy Danyow on this and we tried to find the best way to represent validation in an Aurelia plugin. Instead of starting fresh with a new validation runtime we chose to start with a popular server/browser pure validation library called validate.js. Validate.js has been a great library to work with. I've really enjoyed how fluid and clean setting up validation has been and I'm happy to say that our validation integration makes it even nicer.

Technical Requirements for Validation

We had a few simple requirements from the beginning. Here's a summary:

  1. Must run on server (node.js) and browser.
  2. Use existing pure validation library.
  3. Installable as an Aurelia plugin.

You can read about more of the technical requirements here.

Today's Alpha Release

I think we are at a point where we want to get heavy user feedback on the new validation system, so as of today you can install the alpha release:

If you are using JSPM:

$ jspm install aurelia-validatejs

If you are using NPM:

$ npm install aurelia-validatejs --save

Once the package is installed, then install the plugin in your main.js configure method:

export function configure(aurelia) {  
  aurelia.use
    .standardConfiguration()
    .plugin('aurelia-validatejs');

  aurelia.start().then(() => aurelia.setRoot());
}

Applying Validation with Decorators

Once the plugin is installed, you can use it in your models and view-models with decorators:

import {required} from 'aurelia-validatejs';

export class MyModel {  
  @required name = '';
}

Notice that name on instances of MyModel will be required.

Applying Validation with the Fluent API

If you prefer not to use decorators (or can't), you can also use the fluent API as well:

import {Validator} from 'aurelia-validatejs';

export class Fluent {  
  static inject = [Validator];

  constructor(validator) {
    this.model = new AnotherModel();
    this.validator = validator
      .ensure(this.model, 'firstName')
        .required()
        .length({minimum: 3, maximum: 10})
      .ensure(this.model, 'lastName')
        .required();
  }
}

Note that both the decorator and fluent API take the exact same configuration options.

Displaying Errors

Currently, to display errors you can use the ValidateBindingBehavior which watches for changes to the validation properties of the object and uses a ValidationRenderer to show them. We would like some feedback on the setup so try it out and let us know what you think.

Use the validate binding behavior in your HTML to show errors like this:

<template>  
  <form class="container">
    <div>
      <label class="form-group">
        <strong>Name</strong>
        <input class="form-control" value.bind="model.name & validate" />
      </label>
    </div>
  </form>
</template>  

By default, the renderer uses the standard Bootstrap error formatting.

ValidationReporter

In your view-model code you can watch for errors emitted by the error reporter:

import {required, ValidationEngine} from 'aurelia-validatejs';

export class MyModel {  
  @required name = '';
}

export class MyViewModel {  
  constructor() {
    this.model = new MyModel();
    this.reporter = ValidationEngine.getValidationReporter(this.model);
    this.reporter.subscribe(result => {
      // result is array of errors    
    });
  }
}

What's Next?

Weekly Releases

This first release is to get some initial feedback. Next week we plan to release on Tuesday with some nice improvements in renderers. Right now, for example we are inferring the reporter from the binding engine but we have some planned improvements for the coming week. We hope to continue iteratively releasing improvements and welcome community contribution.

More Complex Renderers

We plan to add more complex renderers soon. We will include mechanisms for showing validation errors on forms as well as ones that are scoped to the current element. We definitely want to welcome community contributions to enable rendering in other scenarios besides the default Bootstrap ones. Building renderers are really simple and we'll have more information on that soon.

Feedback

In the repository for the validate.js bridge you can track issues or provide feedback. We've added a new tag contribution-welcome to indicate that a particular piece of the code-base is stable and contributions are welcome. You'll also note that we've added some initial issues to indicate what we have planned already.

How can you contribute?

I've put together another blog post detailing how validation is currently structured to give others an idea of how things currently work and what we can do to improve validation as we go forward.

Here are some ways to get started contributing if you are interested:

  1. Bug repros / fixes
  2. Unit test coverage
  3. Add TypeScript types
  4. Documentation
  5. Descriptive errors (I'd like for errors to have links to trouble-shooting sections of docs for developers)
  6. Make it work with other CSS libraries

Custom Renderer

Here's a few tips on creating a custom renderer to try out with your CSS framework of choice:

validate-binding-behavior.js currently gets the renderer from validation-renderer.js. To try out your own renderer you can simply copy the validate-binding-behavior and update the name. Change the import to a renderer of your choosing (follow the same patterns in validation-renderer.js of renderErrors / unrenderErrors method signatures for now). We'll have improvements to this and more information soon.

Thanks again for everyone's patience and help! Please report any issues you are having!


As you can see, Patrick has been busy working on the new validation library. I'm excited about what is going to be possible and I hope you'll try it out and help us fill in the missing pieces.

New Testing Capabilities

Previously, we announced that we're working on a library for easy testing of components. With today's releases we also have the first version of aurelia-testing available. With it you can easily stage a custom element or custom attribute in isolation inside a mini Aurelia application, assert how it responds to databinding and assert its behavior throughout the component's lifecycle (bind, attached etc). This library is still in an early stage but please take it for a spin, write some tests for your components and give us feedback.

To get started:

If you are using JSPM:

$ jspm install aurelia-testing

If you are using NPM:

$ npm install aurelia-testing --save

Once you've got the library installed, you can use it in a unit test. Here are a few examples of what it can do:

Given you have following custom element:

my-component.js

import {bindable} from 'aurelia-framework';

export class MyComponent {  
  @bindable name;

  bind() {
    this.something = 'bind';
  }

  attached() {
    this.something = 'attached';
  }

  unbind() {
   this.name = null;
  }

  detached() { }
}

my-component.html

<template>  
  <div class="name">${name} ${something}</div>
</template>  

Some basic tests could look like:

import {StageComponent} from 'aurelia-testing';  
import {MyComponent} from '../src/my-component';

describe('MyComponent', () => {  
  let component;

  beforeEach(() => {
    component = StageComponent
        .withResources('src/my-component')
        .inView('<my-component name.bind="name"></my-component>')
        .boundTo({ name: 'Foo' });
  });

  afterEach(() => {
    component.dispose();
  });

  it('can render the component', done => {
     component.create()
     .then(() => {
       const nameElement = document.querySelector('.name');
       expect(nameElement.innerHTML).toBe('Foo attached');
     })
    .then(done);
  });

  it('can bind with a new context', done => {
     component.boundTo({ name: 'Bar' }).create()
     .then(() => {
       const nameElement = document.querySelector('.name');
       expect(nameElement.innerHTML).toBe('Bar attached');
     })
     .then(done);
  });

  it('can manually handle lifecycle', done => {
    let nameElement;

     component.manuallyHandleLifecycle().create()
     .then(() => {
       nameElement = document.querySelector('.name');
       expect(nameElement.innerHTML).toBe(' ');
     })
     .then(() => component.bind())
     .then(() => {
       expect(nameElement.innerHTML).toBe('Foo bind');
     })
     .then(() => component.attached())
     .then(() => {
       expect(nameElement.innerHTML).toBe('Foo attached');
     })
     .then(() => component.detached())
     .then(() => component.unbind())
     .then(() => {
       expect(component.viewModel.name).toBe(null);
     })
     .then(() => component.bind({ name: 'Bar' }))
     .then(() => {
       expect(nameElement.innerHTML).toBe('Bar bind');
     })
     .then(() => component.attached())
     .then(() => {
       expect(nameElement.innerHTML).toBe('Bar attached');
     })
     .then(done);
  }); 
});

As you see, the test helper lets you easily push components through their lifecycle, testing various aspects of it at each point along the way.

All the Rest

As mentioned above, we've had a variety of bug fixes in this set of releases as well. We've even had performance enhancements across the board to our binding engine and in some cases the repeat performance has improved by 10x thanks to some excellent work by community member Bazyli Brzóska.

As usual, you can find all the details in the change log below.
Oh, and hang in there, the Release Candidate is right around the corner...

Change Log

aurelia-pal 1.0.0-beta.1.2.1

Bug Fixes

  • AggregateError: better surface inner error information (d2e0ee70)

aurelia-polyfills 1.0.0-beta.1.1.3

Bug Fixes

  • reflect: fix target-is-object check (841a64b5)

Features

  • reflect: add polyfill for defineProperty (c6fbc900)

aurelia-binding 1.0.0-beta.1.3.3

Bug Fixes

  • Binary: handle adding undefined (d2a88ddc, closes #337)
  • CheckedObserver: synchronize on changes to input value (f3147440, closes #320)
  • array-observation: do not notify on pop/shift of empty array (d344831b)

Features

  • camelCase: handle hyphenated names (315cfaa2)
  • logging: warn when property can't be defined (a6457c09)

aurelia-ui-virtualization 0.4.4

Bug Fixes

  • utilities: undefined parentElement in IE11 (c7bb7857)

aurelia-templating 1.0.0-beta.1.2.4 (2016-04-29)

Bug Fixes

  • templating-engine: allow for overrideContext in enhance (49c99edf)

Features

  • ViewSlot:
    • allow removal of many views at once to avoid a race condition (4b1005b9)
    • moving Views across the slot (02e59ef1)
  • view: reference creator container (9431f536)

aurelia-templating-binding 1.0.0-beta.1.2.2

Bug Fixes

  • SyntaxInterpreter: one-way default binding mode for checkbox/radio value (124498c3)

templating-resources 1.0.0-beta.1.2.3

Bug Fixes

  • Repeat: ignore changes after unsubscribe (96b721f9)
  • analyze-view-factory: analyze type's view-factory (3cc65d97)
  • focus: focus on attach (3991d999, closes #199)
  • repeat-utilities: remove unnecessary variable and loop evaluation (504c8e69)

skeletons

  • Lots of improvements across all skeletons, especially webpack skeletons.
View Comments...