Undefined Title

モダンなJavaScriptライブラリ開発、テスト環境への移行 -- Karma + webapck + jasmine

2015-01-04

リリース当初には見つけられなかったツールを使って、自分のJavaScriptライブラリの開発環境を改善できたので久しぶりにエントリー書いた。 最近なんかGithubでstarをつけてくれる人が増えてきて100を超えたのと、 俺のJSライブラリの世界観(2014末版)に触発され、 環境改善のモチベーションがあがったので頑張ってみた。

TL;DR

要求や想定

とりあえず自分がしたいことや現状をまとめる。

つまり、配布用のJS, CSSファイルの生成できて、自サイトのWebアプリで利用でき、 CLIからも参照できるようにしたい、と。できるだけスマートに。

移行前

これで大体自分のやりたいことはできていたが、まだ足りない点や不満な点もあった。

この他にもjQuery pluginのnamespaceを汚染してたり、Gruntfileでのprocess起動がイマイチスマートじゃなかったりとか あるけどそれは局所的な実装の問題で、一番問題だったのは上記の2つ。 新規開発や改善のモチベーションを削ぐものだったのでずっと改善したいと思っていた。

移行後

やっぱりwebpackとKarmaの導入が大きい。

各種設定について

webpack, Karma, Travis CI等の各種設定について書く。

webpack

webpack用の設定はこちら。webpack.config.js

CoffeeScriptとjQueryに依存していて、ビルドに含めたくないのでexternalsセクションに指定している点と、 css, coffee, stylusのloaderを指定している点がカスタマイズしている点。

externals: {
    // require("jquery") & require("coffee-script") are external and available on the global
    "jquery": "jQuery",
    "coffee-script": "CoffeeScript"
},
module: {
    loaders: [
        { test: /\.css$/, loader: "style!css" },
        { test: /\.styl$/, loader: "style-loader!css-loader!stylus-loader" },
        { test: /\.coffee$/, loader: "coffee-loader" },
    ]
},

この設定でwebpackコマンドを実行するとdist/bundle.lib.jsdist/bundle.spec.jsを生成する。 で、Gruntでminifyするとpublic/jumly.min.jsを生成して、これが最終成果物。 ファイル1つでJavaScriptもCSSも含んでいる優れモノだ。このファイルをリポジトリにコミットしてしまって、 Webアプリからはこれを参照するようにしている。

dist/bundle.spec.jsはKarmaを使わずにブラウザでテストを実行したいとき用に生成している。 Karmaを使うと実際の描画がブラウザに一瞬しか表示されないため、今までどおり自分でブラウザを開いてテストを実行する手段を残してある。

Karma

Karma用の設定はこちら。karma.conf.js

karma-webpackプラグインを使ってwebpackでビルドするようにしている。 その際、そのままwebpack.config.jsを使用するとsepcファイルが重複してしまうため、下記のようにspec部分のビルドを削除するようにしている。

webpack: function() {
    var conf = require("./webpack.config.js")
    delete conf.entry.spec;
    return conf;
}(),

JavaScriptで設定ファイルが書けて、かつ、関数を評価してくれる場合、こういうとき助かる。

karma.conf.jsでもうひとつカスタマイズしている点としては以下。 Travis CI上ではautoWathなし、一回走ったら終わり、という設定。

var travis_enabled = process.env["KARMA_OPTIONS"] ? true : false;
...
autoWatch: !travis_enabled,
...
singleRun: travis_enabled,

.travis.ymlで下記のように環境変数を設定しておいてTravis CIでの動作を切り替えている。

- export KARMA_OPTIONS="--browsers ChromeTravis"

ただ、このエントリー書いてる最中にkarma-webpack、1.4.0にアップデートされたみたいなんだけど、 その1.4.0がどうもおかしく、karmaの実行時に以下のようなエラーが出る。

You need to include some adapter that implements __karma__.start method!

とりあえず動いている1.3.1を使うようにして様子見。時間がとれたらバグレポートしたいところ。

Travis CI

Travis CI用の設定はこちら。.travis.yml
実際のビルドはこちら。https://travis-ci.org/tmtk75/jumly/builds/45836086

ChromiumをTravis CIで使用する設定は、モダンぽいJavaScriptテスト環境の構築メモを参考にさせてもらった。 非常に助かりました。まさかTravis CIでChromium使ってテストできるとは思ってなかった。

before_install:
  - export CHROME_BIN=chromium-browser
  - export DISPLAY=:99.0
  - sh -e /etc/init.d/xvfb start

  - export KARMA_OPTIONS="--browsers ChromeTravis"

実はこのエントリー書いてる最中に、上記記事がTLに流れてきて「あー、もう今書いてる俺の記事要らないんじゃないかなー」と思った。 まあ、webpackとか被ってない部分もあったので書き進めて今に至る。

あとは前述したようにjQueryとCoffeeScriptとCSSファイルを外部ファイルとして参照するので、npm, make叩いて自前でインストールしている。

install:
  - npm install
  - make dist/jumly.css vendor/coffee-script.js

CLI(PhantomJS)

改善前はJS, CSSの2ファイルを読み込んでいた。

改善前
<link rel="stylesheet" href="#{rootdir}/views/static/release/jumly.min.css" />
<script src='#{rootdir}/views/static/release/jumly.min.js'></script>

改善後
<script src='#{rootdir}/public/js/es5-shim.min.js'></script>
<script src='#{rootdir}/public/jumly.min.js'></script>

改善後はwebpackのビルドのおかげでJSひとつだけ読めばいいようになったのだが、 副作用として今まで使っていなかったFunction#bindメソッドを使用するようになってしまった。 PhantomJS(現時点で1.9.13)はbindをサポートしていないようでエラーになる。困った。 が、幸いPolyfillのひとつes5-shimを読むようにしたらうまく動いてくれた。

ハマった点

files: [
    'node_modules/jquery/dist/jquery.js',
    'vendor/coffee-script.js',
    'dist/jumly.css',
    'spec/ClassDiagramSpec.coffee',
    ...

webpackはCSSもひとまとめにしてくれるが、なぜかKarma上でwebpackを使用したビルドではCSSが適用されないみたい。 そのために位置関係やサイズに関するテストが失敗してしまっていた。CSSを別で生成し(今までどおりGruntでconcat)、 それをjQueryやCoffeeScriptと同様にKarmaの設定で外部ファイルとして読ませたらCSSが期待通りに適用され、無事テストが通った。

まとめ&これから

あともっとモダンにするのにはこんなとこかな。

せっかく環境を整えたので、今年は中身の改善もしたいところだ。