ANDRIY DACENKO

SOFTWARE ENGINEER

November 4, 2015

JS & FRONT-END AUTOMATION

 PAINLESS DEVELOPMENT

JS MENTORING 2015

FRONT-END

ENVIRONMENT

ITS ALL ABOUT

Compile

Minify

Optimize

Test

PACKAGE MANAGERS

MANAGE NODE

# nvm - node version manager
$ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash
$ nvm install iojs

$ nvm install node

$ nvm alias default node

$ nvm use default
# .nvmrc
v0.12.7
  • Install nvm
  • Set default v4.1.1
  • Set v0.12.7 for
    current project

TEMPLATING

Mustache

handlebars

_.templates

EJS

Hello {{name}}
You have just won {{value}} dollars!
{{#in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}}
{
  "name": "Chris",
  "value": 10000,
  "taxed_value": 10000 - (10000 * 0.4),
  "in_ca": true
}
}
Mustache.render(view, data);
Hello {{name}}
You have just won {{value}} dollars!
{{#if in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/if}}
{
  "name": "Chris",
  "value": 10000,
  "taxed_value": 10000 - (10000 * 0.4),
  "in_ca": true
}
Handlebars.compile(view)(data);
Hello <%= name %>
You have just won <%= value %> dollars!
<% if(in_ca) { %>
Well, <%= taxed_value $> dollars, after taxes.
<% } %>
{
  "name": "Chris",
  "value": 10000,
  "taxed_value": 10000 - (10000 * 0.4),
  "in_ca": true
}
_.template(view)(data);
Hello <%= name %>
You have just won <%= value %> dollars!
<% if(in_ca) { %>
Well, <%= taxed_value $> dollars, after taxes.
<% } %>
{
  "name": "Chris",
  "value": 10000,
  "taxed_value": 10000 - (10000 * 0.4),
  "in_ca": true
}
new EJS({text: view}).render(data);

EJS

<% Embedded JavaScript %>

CSS

Sass

LESS

Stylus

PostCSS

@mixin border-radius($radius...) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  border-radius: $radius;
}
@import "vendor";

body {
  font: 12px Helvetica, Arial, sans-serif;
}

a.button {
  @include border-radius(5px);
}
.border-radius {
  -webkit-border-radius: @arguments;
  -moz-border-radius: @arguments;
  border-radius: @arguments;
}
@import "vendor";

body {
  font: 12px Helvetica, Arial, sans-serif;
}

a.button {
  .border-radius(5px);
}
border-radius()
  -webkit-border-radius arguments
  -moz-border-radius arguments
  border-radius arguments
@import 'vendor'

body
  font 12px Helvetica, Arial, sans-serif

a.button
  border-radius 5px
@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  border-radius: $radius;
}
@import "vendor";

body {
  font: 12px Helvetica, Arial, sans-serif;
}

a.button {
  @include border-radius(5px);
}

POSTCSS

TASK

  • Init package.json

  • Create index.js

  • Install PostCSS

  • Install autoprefixer plugin

  • Run PostCSS

Estimates: 10 min

JAVASCRIPT

Concat

Browserify

Webpack

System.js

$ npm install -g browserify
// main.js
var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

console.log(unique(data));
$ npm i uniq --save


$ browserify main.js -o bundle.js
$ npm install webpack -g
// main.js
var unique = require('uniq');

var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

console.log(unique(data));
$ npm i uniq


$ webpack ./entry.js bundle.js

WEBPACK

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    path: './build',
    publicPath: 'http://mycdn.com/', 
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.less$/, loader: 'style!css!less' },
      { test: /\.css$/, loader: 'style!css' },
      { test: /\.png$/, loader: 'url-loader?limit= 8192' }
    ]
  }
};
$ npm install jspm -g
// main.js
import 'util';

const data = [1, 2, 2, 3, 4, 5, 5, 5, 6];

console.log(unique(data));
$ jspm i npm:uniq


$ jspm bundle-sfx main

JSPM

TASK

  • Install jspm

  • Install lodash in project

  • Add your own js code

  • Run build of jspm

Estimates: 10 min

TEST

Mocha

Jasmine

Karma

Protractor

$ npm install -g mocha
// test/test.js
var assert = require('assert');

describe('Array', function() {
  describe('#indexOf()', function () {
    it('should return -1 when value is not present', function () {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});
# continous testing
$ mocha -w
$ mocha

  Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (9ms)
$ npm install -g jasmine-node
// test/test.spec.js
describe('Array', function() {
  describe('#indexOf()', function () {
    it('should return -1 when value is not present', function () {
      expect([1,2,3].indexOf(5)).toEqual(-1);
      expect([1,2,3].indexOf(0)).toEqual(-1);
    });
  });
});
# continous testing
$ jasmine test --watch
$ jasmine test

  Array - 4 ms

      #indexOf() - 4 ms
          should return -1 when value is not present - 3 ms

  Finished in 0.01 seconds
  1 test, 2 assertions, 0 failures, 0 skipped
$ npm install -g karma-cli

# Install plugins
$ npm install karma-jasmine karma-phantomjs-launcher --save-dev
// karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [...],
    //...
  });
};
# watch mode
$ karma start


$ karma start --single-run

[karma]: No captured browser, open http://localhost:9876/
[karma]: Karma v0.13.14 server started at http://localhost:9876/
[launcher]: Starting browser PhantomJS
Executed 1 of 1 SUCCESS (0.004 secs / 0.001 secs)
$ npm install -g protractor

$ webdriver-manager update

$ webdriver-manager start
describe('angularjs homepage todo list', function() {
  it('should add a todo', function() {
    browser.get('https://angularjs.org');

    element(by.model('todoList.todoText'))
        .sendKeys('write first protractor test');
    element(by.css('[value="add"]')).click();

    var todoList = element.all(
        by.repeater('todo in todoList.todos')
    );
    expect(todoList.count()).toEqual(3);
    expect(todoList.get(2).getText())
        .toEqual('write first protractor test');

    // You wrote your first test, cross it off the list
    todoList.get(2).element(by.css('input')).click();
    var completedAmount = element.all(by.css('.done-true'));
    expect(completedAmount.count()).toEqual(2);
  });
});

TASK

  • Install mocha

  • Create test folder

  • Add simple test

  • Run build test using mocha

Estimates: 10 min

CUSTOM TASKS

Sprites

Livereload

Minification

CSS

Deploy

Release

Semver

Server

Static

TOOLS

YO!

$ npm i -g yo generator-webapp

$ yo webapp

GENERATOR

$ npm install -g generator-generator

# generate generator
$ yo generator
.
├── generators
│   └── app
│       ├── index.js
│       └── templates
│           ├── _bower.json
│           ├── _package.json
│           ├── editorconfig
│           └── jshintrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── package.json
├── README.md
└── test
    └── test-app.js

GRUNT

module.exports = function(grunt) {  
  grunt.initConfig({
    jshint: {
      files: ['**.js'],
      options: JSON.parse(require('fs').readFileSync('./.jshinrc'))
    }
  });
  grunt.loadNpmTasks('grunt-contrib-jshint');
};

PLUGINS

FLOW

GULP

var jshint = require('gulp-jshint');  
var gulp   = require('gulp');  

gulp.task('jshint', function() {  
  return gulp.src('**.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'));
});

PLUGINS

FLOW

NPM

"devDependencies": {
    "jshint": "latest",
},
"scripts": {
    "lint": "jshint **.js"
}

PLUGINS

FLOW

NPM CONFIG

// package.json
{
  "name": "epam-mentoring",
  "config": {
    "autoprefixer": "latest",
    "cssmin": "latest",
    "reporter": "xunit"
  },
  "scripts": {
    "build:css": "autoprefixer -b 'last 2 versions' < 
        assets/styles/main.css | cssmin > dist/main.css",
    "test": "mocha test/ --reporter $npm_package_config_reporter"
    "test:dev": "npm run test --epam-mentoring:reporter=spec"
  }
}
$ npm config set epam-mentoring:reporter spec

TASK

  • Add test run script

  • Add `jspm install` before build

  • Add build:css

  • Add build:js

  • Run build:* in parallel

Estimates: 10 min

CI

Test

Release

Deploy

TRAVIS

CONFIG

language: node_js
node_js:
  - '0.12'
  - '4'
  - '5'

before_install:
  - npm install -g npm

install:
  - npm install -g bower

DEPLOY & RELEASE

deploy:
  - provider: heroku
    api_key "YOUR HEROKU API KEY"
  - provider: releases
    api-key: "GITHUB OAUTH TOKEN"
    file: "FILE TO UPLOAD"
    skip_cleanup: true
    on:
      tags: true

HA

Simple web page about

  • stack used
  • code coverage report

Stack

  • Use any pre-/post- processor for CSS

  • Use any JS build tool

  • Use any Task runner

  • Use any Test framework/runner

  • Use Travis to deploy / release or both

Q&A Time

ANDRIY DACENKO

SOFTWARE ENGINEER

November 4, 2015

JS & FRONT-END AUTOMATION

 PAINLESS DEVELOPMENT

JS & Front-End automation

By Andrew Dacenko

JS & Front-End automation

EPAM: JS Mentoring program Kyiv.2015-06. Lecture20: JS and Front-End automation

  • 1,348