Saturday, November 25, 2017

Vue.js - the jQuery killer

Vue.js is awesome. It really is. There is however this notion that frameworks such as Angular or React are primarily for full-fledged, single-page frontend applications and that notion carries over to Vue. In this installment I am going to try to share with you a way to treat Vue as a better jQuery and to have more than a few micro-apps on one page.

The jQuery phenomena

There are more than a few reasons why jQuery became so popular. One of them was the need to target multiple browsers with one, coherent codebase. I think that this is exactly why there are so many jQuery developers out there and so few people (by comparison) that can actually use JavaScript and the DOM API. Fortunately enough, the browser wars led to standards being formed and implemented by most major browsers and now things like searching for elements in the DOM tree or toggling a class is not a challenge anymore. Let's face it: even Ajax (the biggest example of fallout from browser wars) is standardized these days! And I am not talking here about the XMLHttpRequest class but about the fetch API which makes jQuery pretty much useless.

Welcome to the 21st century

So where do frameworks like React, Angular or Vue fit in this new, jQuery-free world? Is there even a place for them? Well, as it turns out 2 things have changed over the past decade: not only browsers became more standardized but also the applications became way more demanding. It's not just a few plugins bashed together like it used to be back in the days. Now we see browsers as an operating system / development platform of its own that can target multiple hardware and deliver experiences (not "pages") wherever we go. That's the price to pay for being cool :)

For that reason the size of a codebase that use jQuery alone grows out of proportion trying to synchronize the state of multiple deeply nested components. This is not what jQuery was designed for and it's also where the scaling capabilities of it end. We need something that will match the new requirements. And so along came a long list of failures in the industry trying to solve the misery of big apps. At first it looked like the MVC pattern (so popular in the backend world) would be a good fit. I think the main reason was the overwhelming presence of design patterns in the late 2000s. And MVC was (along with Singleton) among the most recognized ones. Even good frameworks like ExtJS went to the dark side and implemented the MVC pattern. Such a shame!!!

That didn't solve the scaling problem. Apps were growing out of proportion of what was feasible at that time. It wasn't until about 2013 when Facebook's engineer Jordan Walke came up with this idea of shedding MVC in the frontend all together and replacing the ever growing connections between UI and data with mathematical precision and unidirectional data flow that we all now take for granted.

The framework problem

As it turns out the problem with frameworks is that they take control from you and let you react only when something happens. This is mostly great but sometimes it means global state needs to be maintained to gain access to the underlying data. React does away with this by embedding the state directly in components. This was, in my opinion, the biggest step forward after the definition of unidirectional data flow. However, with React came JSX (to make our life easier) and that came with Babel pre-processor or TypeScript and other goodness like webpack, gulp, grunt to be the main driver for the build process. In short: the build process became mandatory. Something you have never had to talk about when doing just jQuery. To make matter even more interesting now that we had the app it was usually so heavy (take angular 2 hello app built with the daemonic spin-off form Ember CLI) that having more than one app was virtually technically impossible.

To even approach the level of reusability jQuery provides we would need to get into the realm of tens or hundreds of apps on one page! That feels right down insane but if you come to think of it, it really opens up possibilities where previously only jQuery would fit.

The Vue

Such is the case with Vue.js. It is a small library (not necessarily a framework) that doesn't require any build steps, runs directly from the browser but if required it can grow to gigantic proportions by the share virtue of composition of components. Yes, that is right, if used with the component mindset it will easily grow beyond what was possible with jQuery (or plain old JavaScript for that matter). But it can do small bits as well! And it does it with such a grace it is absolutely stunning!

Let's see the simplest app that would run in the browser:

    new Vue({
      el: '#app',
      render: h => h({ template: '<h1>Hello, world!</h1>' })
    })

Assuming there is an element with id '#app' the app will replace that element with an H1 with the text Hello, world!. The el element doesn't need to be a CSS selector (if it would be it needs to point to one and only one element or the rest will be discarded). It can, however, be a DOM object, like so:

    new Vue({
      el: document.querySelector('#app'),
      render: h => h({ template: '<h1>Hello, world!</h1>' })
    })

Now assuming we would like to instantiate this application in every container with the class app applied we would need to do something that resembles this:

    [ ...document.querySelectorAll('.app') ].forEach(el => {
      new Vue({
        el,
        render: h => h({ template: '<h1>Hello, world!</h1>' })
      })
    })

Easy, right? Because the querySelectorApp returns an array-like object that doesn't provide the forEach method we're spreading that list over an array literal effectively creating a proper array from the list of found elements. Next, we apply the Vue application on each of the found placeholders. This is very much what jQuery was doing to us for all these years! Applying a transformation to all selected elements! Sure, it's true jQuery was less invasive (if there is anything present in the <div class="app">...</dic> Vue will go and nuke that content with its own content). But is it not like we would like to have less and less HTML managed by the backend (preferably just a placeholder for what is an app that is fully managed by the browser?

Of course the other question on the horizon is passing on information to those small apps. There is a few ways to do it (Ajax being one of them) that can help you here. My favorite is for the backend to use either a JavaScript object somewhere on the page that can be easily matched with the container. Other example (especially useful if there are just a few parameters you need to pass in) is to use the data-* attributes. Just don't go overboard with it!

Post mortem

I think that jQuery is one of the few frontend utilities that profoundly changed the way we think about software development. However the time is, finally, almost up because of the requirements new Internet applications. There is an obvious need for tools that solve problems that are not yet natively solved in the browsers in such a way that would be appropriate for the broad Internet developers audience. React and Vue are fitting nicely in that niche and do a fantastic job in being lean, focused. That gives the opportunity to do a lot more with Vue than was previously possible and to use those new tools in ways that would previously not even cross one's mind.

Happy coding

Wednesday, November 22, 2017

Writing unit tests with Vuejs and Poi

Poi is great. It's fantastic! It's one of the tools that were very much missing from the landscape. Now that we already have it let's explore what we can do with it. Let's get serious!

This time we're going to create a POI-based project that will support both Vue and unit testing.

First we need to have a project. A simple npm init will do. When asked to give the test command write poi test and you're done.

$ npm init
...
test command: poi test
...

Is this ok? (yes)

That was easy. Now let's install the required dependencies (there are quite a few of them but we'll go through the list and I will explain what each one does):

$ npm install --save-dev poi poi-preset-karma \
    webpack karma mocha chai karma-webpack karma-mocha karma-chai \
    vue vue-test-utils

poi is our build system so it is obvious it needs to be installed. poi-preset-karma is a preset for poi for it to know what to do when you execute poi test. It will use karma to run the tests.

webpack and karma-webpack allow to use webpack to bundle all the files

mocha is the testing framework giving you BDD-like tests with describe and it. karma-mocha is a package that allows easy integration of Karma and Mocha.

chai is a fantastic library for writing expectations. Similarly to Mocha, karma-chai makes it easy to integrate Karma with Chai.

And last but not least we top it with some Vue sauce. vue is our beloved frontend framework and vue-test-utils is a library that aids testing.

Let's write some tests

The default folder structure assumes unit tests to be located in test/unit folder and the files to be named like something.test.js. So let's create one:

test/unit/example.test.js

it('will pass', () => {
  expect(1).to.equal(1)
})

Easy, right? For Poi to know what to do when we run poi test we need to use a preset. To do that we'll create a poi.config.js file like so:

module.exports = {
  presets: [
    require('poi-preset-karma')({
      frameworks: [ 'mocha', 'chai' ]
    })
  ]
}

As you can see there we use Chai asserts. For Mocha to know about Chai we need to tell the Karma preset that both Mocha and Chai are to be used.

Now all that remains is to run the tests. There are 2 options for running:

  • Run just once and finish
  • Run continuously in the background and re-run tests when files change

To just run the tests and finish you issue the command

$ npm test

This works because test is a top-level npm command.

To run tests continuously with automatic re-running do this:

$ npm test -- --watch

Let's decipher the bits. npm test is pretty much obvious at this point. The double-dash is a way to tell npm hey, when you run the command specified in the scripts section then add all the parameters that follow to the execution. In our case the --watch parameter enables watching changes on files and automatic re-running of all the tests.

Poi and continuous integration server

Running tests in a real browser is great because they will be testing components in their natural habitat. It poses however a difficult problem when running them on a headless continuous integration server. This is a common problem and to address that Poi has a switch that uses ChromeHeadless instead of the regular Chrome:

$ npm test -- --headless

Of course you can combine them and run headless tests in watch mode on a remote machine via SSH using Vim or Emacs as your text editor - especially if your primary work computer is running Windows and you want things to go fast(er). The possibilities are endless.

Debugging

By default tests are being executed on Google Chrome. The page you'll see contains a little DEBUG button. When you click it a new tab will open where you can set breakpoints and examine your code. One thing to note is that by default there is a limit to how long one unit test can take (2s) and you will most likely exceed that limit if you pause in a test method. Don't be alarmed by that - just re-run your tests without breakpoints and you'll see if it works or not.

Please note that the tests in debug mode won't re-run by themselves when you change things in the sources. You need to refresh the page to start a new test run. This is done so that hot-module reloading doesn't kick in in the middle of your debug session. Very convenient!

Let's go Vueing

The time has come to make a real test that uses Vuejs. So let's create one in test/unit/components/Clicker.js that will check if upon clicking on the component a custom event will be emitted:

import Vue from 'vue'
import { mount } from 'vue-test-utils'

import Clicker from '@/components/Clicker.vue'

describe('Component: Clicker', () => {
  it('will emit "clicked" event when interacted with', () => {
    // given
    const wrapper = mount(Clicker)

    // when
    wrapper.vm.$el.click()

    // then
    expect(wrapper.emitted().clicked).to.deep.equal([ [ 'Here!' ] ])
  })
})

Now in the root folder let's create src/components and inside that let's create our component as a single-file component Clicker.vue:

<template>
  <h1 @click="$emit('clicked', 'Here!')">Hello!</h1>
</tempalte>

That's it! It is really that simple!

Post scriptum

This barely scratches the surface of what is possible with Poi but at the same time it does cover a lot of configuration that you don't need to write yourself. Just looking at the starter template for webpack-simple shows how much is being done behind the scenes.

Happy testing!

Tuesday, November 14, 2017

Making xdg-open use Chrome instead of Firefox

One of the more annoying things in Linux Mint that I came across is the fanatic persistence of using Firefox to open links after making Chrome the default browser. I know, I know... Google spying, I'm giving up privacy to some corpo that will do evil things with my sexual preferences, yadda, yadda, yadda - I don't care. I want my links to open in Chrome. Period.

Recently I came across a post on askubuntu.com that explains what needs to happen. Basically all URL opening mechanisms are using xdg-open underneath the covers so all it takes is to teach it the lesson it needs:

$ xdg-mime default google-chrome.desktop text/html
$ xdg-mime default google-chrome.desktop x-scheme-handler/http
$ xdg-mime default google-chrome.desktop x-scheme-handler/https
$ xdg-mime default google-chrome.desktop x-scheme-handler/about

Done. Now ctrl-click any links in the console and you'll have that opened in Chrome.

Happy clicking!