Showing posts with label poi. Show all posts
Showing posts with label poi. Show all posts

Thursday, December 7, 2017

POI and naming classes in CSS Modules

This will be a quick one. If you're trying to use CSS modules with POI in a Vue.js app and you want to exercise some control over what the names of the generated classes are then there is a configuration option in POI for that. It's a little bit hidden so it took mi like 5 minutes to figure it out - hence the post.

module.exports = {
  vue: {
    cssModules: {
      localIdentName: '[hash:base64:5]'
    }
  }
}

That weirdness stems from the fact that POI configures the css-loader indirectly via the vue when setting up the vue-loader. We can take advantage of the fact that it uses the Object.assign method and sneak in our configuration option.

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!

Saturday, October 28, 2017

Poi that!

You might already be familiar with Webpack. It's a great tool and a bastard when it comes to configuration. It's a beast! It's a daemon! It's one of those sons of bitches that you can't loose and that will hunt you down everywhere you go. Like a woman! Just look at the amount of configuration code that the vue-cli produces when used in combination with the webpack template. That is right down INSANE!!!

Although there's not much that can be done with woman, Webpack has received a shell that is finally making some sense! That shell is called POI and is created by a gentleman who goes by the name of Egoist. Let's take a closer look at what it does.

At first glance it is a simplified build tool. One that you would probably never look upon knowing what kind of power Webpack brings to the table. But if you look a bit closer it turns out it is just (easier said than done) a box around webpack configuring it to some standards using a set of predefined presets for the most common tasks. However, knowing what kind of a daemon sits in the guts of it, you'll be pleased to know that poking around that dragon is very much possible and easy at the same time.

So what does it look like to use it? First you need to know that Poi stems from the ashes of a slimming treatment of vue-cli. This means it will out-of-the-fricken-box work just perfect with Vue.js! All you need is a file that will bootstrap your application. Let's see the content of an index.js that does it.

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

And then the single-file-component App.vue

<template>
  <h1>Hello, world!</h1>
</template>

You can obviously put everything else in there (the <script> and <style> sections for example).

Now let's see how do we actually use Poi with that nice application. First of all you need to install Poi. For the sake of this oversimplified and butt-ugly example we'll perform the worst voodoo magic ever and install Poi globally. But please, beyond demonstration purposes NEVER EVER EVER EVER DO THAT. EVER. Poi is your build system so it must be part of your project. You have been warned!

npm install -g poi

Now having index.js and App.vue in that same directory run the following command:

$ poi index.js

# some useless output here

                               Asset       Size  Chunks                    Chunk Names
abf8058814c81f7a160b.hot-update.json   44 bytes          [emitted]         
                           vendor.js     1.5 MB       0             [big]  vendor
                           client.js    13.6 kB       1                    client
                         manifest.js    31.2 kB       2  [emitted]         manifest
ff42074b3ef0f4f41971.hot-update.json   35 bytes          [emitted]         
                          index.html  578 bytes          [emitted]         

> Open http://localhost:4000
> On Your Network: http://127.0.0.1:4000

 DONE  Build 664748 finished in 141 ms!

That is it! Nothing more, nothing less! You have access to everything that you would expected: Hot module reloading, bundling app in separate files so that the part that actually changes is not the whole package, an index.html that is automatically generated for you to host your application - you name it!

There is much more to poi than just running the project. For example to be able to run unit tests you need to start using poi presets. That's not very hard - requires a few more packages though.

$ npm install poi-preset-karma karma-webpack karma-mocha karma-chai webpack mocha chai

With those packages installed we need to make Poi aware how to run tests. We do that by creating a poi.config.js like so:

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

That's really it! Nothing more! Just basically 4 lines telling that we would like to use karma with mocha and chai. Jeez! That is truly magnificent! Totally Vue.js-style! Now let's have the simplest test possible. All unit tests are to be stored in test/unit folder. This location can obviously be further configured but for now it will do just fine. So let's create test/unit/example.test.js like so:

import { expect } from 'chai'

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

Having that in place let's run the tests!

$ poi test

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 526 ms!

28 10 2017 13:22:30.310:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:22:30.310:INFO [launcher]: Launching browser Chrome with unlimited concurrency
28 10 2017 13:22:30.314:INFO [launcher]: Starting browser Chrome
28 10 2017 13:22:30.892:INFO [Chrome 62.0.3202 (Linux 0.0.0)]: Connected on socket BUV....
  ✔ will work

Finished in 0.002 secs / 0 secs @ 13:22:31 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed

Now ain't that a pretty! I myself was looking to simplify the use of webpack for quite some time and it is only my bad luck that I didn't run into poi sooner. I would have saved a whole lot of time! Tumbling down the rabbit hole if you would like to run those tests in headless mode you can specify the browser to use. I like headless Chrome:

$ poi test --browsers ChromeHeadless

Boooom! Your tests can now be executed in headless environments like CI! That is so easy and soooo exciting!

Want to do some code coverage?

$ poi test --coverage

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 508 ms!

28 10 2017 13:46:33.489:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:46:33.489:INFO [launcher]: Launching browser ChromeHeadless with unlimited concurrency
28 10 2017 13:46:33.493:INFO [launcher]: Starting browser ChromeHeadless
28 10 2017 13:46:33.747:INFO [HeadlessChrome 0.0.0 (Linux 0.0.0)]: Connected on socket 9Xjcc...
  ✔ will work

Finished in 0.006 secs / 0.001 secs @ 13:46:33 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed
----------|----------|----------|----------|----------|----------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
----------|----------|----------|----------|----------|----------------|
All files |      100 |      100 |      100 |      100 |                |
----------|----------|----------|----------|----------|----------------|

So one last thing that we need is to deliver our super duper app to some hosting. scp-ing files is not the task for Poi but building those files is and Poi does it very well:

$ poi build index.js

You will end up with the dist folder containing everything. One property of the created files is that they use absolute paths for resources like scripts and styles. It's great when it comes to hosting but trying it out using the file:// protocol (simply opening the index.html file in the browser) won't work. But it is very easy to fix. In poi.config.js add the webpack method like so:

module.exports = {
  // ...
  webpack(config) {
    config.output.publicPath = ''
    return config
  }
}

Now run the build again and open the resulting index.html file in the browser. Boom! It works!

There is way more to Poi than that. Check out the official documentation at http://poi.js.org and the GitHub page at http://github.com/egoist/poi. You will be amazed what's in store!

Happy... poi'ing!