Configure Pact JS Consumer Contract tests in Angular

This post describes the steps to configure Karma on an Angular 4+ project to run consumer contract tests using Pact.

1) Install pact and pact-web
yarn add pact –dev
yarn add pact-web –dev

Pact JS is a javascript library to create consumer-driven contract tests.
Pact-web is a module to support Pact in non-Node environments such as Karma.

2) Install Karma Pact
yarn add @pact-foundation/karma-pact –dev

Under the covers karma-pact will be in charge of starting a pact-mock-service in the port localhost:1234 before the tests execution as well as shutting it down when karma finish.

3) Configure Karma to support consumer contract tests
karma.conf.js

frameworks: ['jasmine', '@angular/cli', 'pact'],   // 1) add pact framework
plugins: [
  require('karma-pact'), // 2) add karma-pact plugin
  ...
]
...
pact: [{cors: true, spec: 2, port: 1234, dir: 'pacts/'}],  // 3) configure one or multiple pact-mock-services here
proxies: { // 4) here we can define proxies to redirect requests from our pact tests to the mock server
  '/api/tasks': 'http://localhost:1234/api/tasks'
},

Notice: In some situations, it may be useful to run multiple Pact Mock Servers. For instance, when running tests with karma, if a consumer defines contracts for several providers, the generated Pact contracts may be mixed up. A solution to solve this is to run one Pact Mock Server per provider. Support for multiple services was introduced on karma-pact v1.0.0.

After a successful consumer contract test run, Pact generates contract files and saves them in the /pacts folder.

Run consumer contract tests

At this stage, the project is configured and ready to run contract tests along the rest of unit tests. Tests can be run as usual with
ng test

You can find a working example of this configuration in the next sample app:
https://github.com/paucls/task_list_ui-angular/tree/feature/pact-consumer-contract-tests

In my next article, I explain in more detail how to implement Consumer-Driven Contracts.

Documentation
https://github.com/pact-foundation/pact-js#using-pact-in-non-node-environments
https://github.com/pact-foundation/pact-js/issues/20
https://github.com/pact-foundation/karma-pact
https://github.com/koelec/angular-pact-example
https://github.com/NomiJ/Pact-Angular2-Example

Advertisements

Jasmine-Matchers – Adding more common matchers to Jasmine.

Jasmine-Matchers is a library of test assertion matchers for a range of common use-cases, to improve the readability of tests written using the Jasmine testing framework.

Installation

Install the package

npm install karma-jasmine-matchers --save-dev

And configure it in karma.conf in the frameworks section

frameworks: ['jasmine', 'jasmine-matchers’],

To use the additional matchers also in protractor e2e, protractor should be configured to import the ‘jasmine-expect’ module before the tests run. That can be done in the protractor.conf.js like this:

onPrepare: function () {
// Import additional jasmine matchers
require('jasmine-expect’);
}

Usage

The list of additional matchers is available here https://github.com/JamieMason/Jasmine-Matchers#matchers

Examples

Examples of unit test refactors using new matchers

toBeArrayOfSize
– expect(clients.length).toBe(2);
+ expect(clients).toBeArrayOfSize(2);

and instead of failing like this
Expected 1 to be 2.
when it fails, the new matcher look like this:
Expected [ Object({ id: ‘client-id’, name: ‘A Client’ }) ] to be array of size 2.

toBeEmptyString
– expect(vm.getCategory({})).toBe(‘’);
Expected ‘Test’ to be ‘’
+ expect(vm.getCategory({})).toBeEmptyString();
Expected ‘Test’ to be empty string.

– expect(vm.projectsFilter.name).toEqual(‘’);
Expected ‘Test’ to be ‘’
+ expect(vm.projectsFilter.name).toBeEmptyString();;
Expected ‘Test’ to be empty string.

toBeEmptyObject
– expect(data).toEqual({});
Expected Object({ location: ‘ftp.mysite.com’, username: ‘username’, password: ‘password’, directory: ‘test’ }) to equal Object({ }).
+ expect(data).toBeEmptyObject();
Expected Object({ location: ‘ftp.mysite.com’, username: ‘username’, password: ‘password’, directory: ‘test’ }) to be empty object.

toBeTrue
– expect(vm.showHelp).toBe(true);
Expected false to be true.
+ expect(vm.showHelp).toBeTrue();
Expected false to be true.

Documentation

https://github.com/JamieMason/Jasmine-Matchers
https://www.npmjs.com/package/karma-jasmine-matchers
https://blog.pivotal.io/labs/labs/writing-beautiful-specs-jasmine-custom-matchers
https://github.com/JamieMason/Jasmine-Matchers/issues/60

Adding a Swagger Validator Badge to you project README

The Swagger Validator project can be used to show a “valid swagger” badge on your site or github README file.

captura-de-pantalla-2016-11-06-a-las-7-23-54

To generate a badge image for the validation of your swagger json or yaml file against OpenAPI 2.0 spec use

<img src="http://online.swagger.io/validator?url={YOUR_URL}">

Or using markdown

[![swagger-api validator-badge]({YOUR_URL/api_spec.yaml}task-list-api-swagger-definition.yaml)](./api_spec.yaml)

Links:
https://github.com/swagger-api/validator-badge
https://github.com/swagger-api/validator-badge/issues/77

Decorating Angular $httpBackend service.

An example of how to ​decorate the angular $httpBackend mock server with custom logic. In this example, I log to console the request method and URL.

// Configure the Mock HTTP Backend
angular
    .module('my-app')
    .config(['$provide', function ($provide) {
        $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
    }]);

// Decorate Mock HTTP Backend to log requests
angular
    .module('my-app')
    .config(function ($provide) {
        $provide.decorator('$httpBackend', function ($delegate) {
            let decoratedHttpBackend = function (method, url, data, callback, headers, timeout, withCredentials, responseType) {
                console.log(method + ' ' + url);

                return $delegate.call(this, method, url, data, callback, headers, timeout, withCredentials, responseType);
            };

            for (var key in $delegate) {
                if ($delegate.hasOwnProperty(key)) {
                    decoratedHttpBackend[key] = $delegate[key];
                }
            }

            return decoratedHttpBackend;
        });
    });

Solve CORS Cross-origin issue in Development Environment

Developing a front-end application, let’s say an Angular app, you could run into CORS Cross-origin issues performing requests to back-end services.

XMLHttpRequest cannot load http://my-service/api/my-resource. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8000&#8217; is therefore not allowed access.

In production is something that should be configured in the server adding the following header to its response:

Access-Control-Allow-Origin: *

And the same can be done in your local dev machine if you are serving the fron-end and also the back-end. But what happens if your service is in another machine or port?

We can solve this problem with a local reverse proxy, or using apps like Burp Suite. But a really quick workaround is to use the Google Chrome Allow-Control-Allow-Origin plugin.

Don’t forget to configure properly the filters to intercept only the URLs to the services that you use:

http://my-service/api/my-resource/*

CORS_chrome_plugin

Another hacky and quick way to solve this is opening chrome with the web security disabled:

open /Applications/Google\ Chrome.app/ --args --disable-web-security

but It could be really dangerous to use that all the time.

To know more about CORS read http://www.html5rocks.com/en/tutorials/cors/.

Bower Guideline – Always Ignore the files that you don’t want to publish

The bower.json defines several options, one of those is the ignore option:

ignore [array]: An array of paths not needed in production that you want Bower to ignore when installing your package.

It is really important to use it when you are defining a bower.json of a package that is meant to be used from other apps or modules.

Why?
When a consumer installs your package via Bower, it will download your project’s entire git repository into their project. But in most of the cases, we don’t want to distribute everything. The consumer app doesn’t need things like​ tests, configuration files, tasks, etc. Only the original and minified versions of the component and the documentation. That will save time downloading the module and disk space.

Guideline
The rule of thumb is to use the ignore attribute to define the list of files and directories that we don’t want to publish.

For example:

"ignore": [
"gulp",
".bowerrc",
".gitignore",
"gulpfile.js",
"package.json",
"README.md"
],

Angular-ui-grid enable filtering from grid menu

In Angular ui-grid it is possible to configure the grid with filtering using the enableFiltering option.

vm.gridOptions = {enableFiltering: true};

I wanted to allow the user to hide/show the filter clicking a button.
There is an example of that in http://ui-grid.info/docs/#/tutorial/103_filtering, but I wanted to add that action to the grid menu itself.

angular-ui-grid-enable-filtering-grid-menu-01

angular-ui-grid-enable-filtering-grid-menu-02

Here is the code of my solution:

            vm.gridOptions.gridMenuCustomItems = [
                {
                    title: 'Hide filter',
                    icon: 'glyphicons filter failure',
                    leaveOpen: true,
                    order: 0,
                    action: function ($event) {
                        this.grid.options.enableFiltering = !this.grid.options.enableFiltering;
                        this.grid.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN);
                    },
                    shown: function () {
                        return this.grid.options.enableFiltering;
                    }
                },
                {
                    title: 'Show filter',
                    icon: 'glyphicons filter success',
                    leaveOpen: true,
                    order: 0,
                    action: function ($event) {
                        this.grid.options.enableFiltering = !this.grid.options.enableFiltering;
                        this.grid.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN);
                    },
                    shown: function () {
                        return !this.grid.options.enableFiltering;
                    }
                }
            ];

You can see that doesn’t need to expose the grid to scope in a onRegisterApi block.

Documentation:
http://ui-grid.info/docs/#/tutorial/103_filtering
http://ui-grid.info/docs/#/tutorial/121_grid_menu