かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その45 )( gradle の build タスク実行時に Javascript の build+テスト を実行する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( 番外編 )( MobX を使用してみる2 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Flow を試してみようと思っていましたが、途中からちょっと入れて試すようなものではなさそうだったので、止めました。
    • Gradle の build タスクを実行した時に、Jest のテストと webpack による build を一緒に実行できるようにする(エラーが出た場合には build タスクを中断する)方法を調べます。

参照したサイト・書籍

  1. gradle-node-plugin 使って Sass をコンパイルしたりすればいいじゃない
    https://qiita.com/macoshita/items/82aa006c75cdd2222abf

  2. srs/gradle-node-plugin
    https://github.com/srs/gradle-node-plugin

  3. jest-html-reporter
    https://www.npmjs.com/package/jest-html-reporter

  4. How can I set NODE_ENV=production on Windows?
    https://stackoverflow.com/questions/9249830/how-can-i-set-node-env-production-on-windows

  5. cross-env
    https://www.npmjs.com/package/cross-env

  6. webpack-contrib/uglifyjs-webpack-plugin
    https://github.com/webpack-contrib/uglifyjs-webpack-plugin

  7. GoogleChromeLabs/webpack-training-project
    https://github.com/GoogleChromeLabs/webpack-training-project

  8. GoogleChromeLabs/webpack-libs-optimizations
    https://github.com/GoogleChromeLabs/webpack-libs-optimizations

目次

  1. 目標
  2. gradle から npm scripts を実行するのに gradle-node-plugin を使用する
  3. gradle から npm scripts を実行するのに task ...(type: Exec) {...} を使用する
  4. jest-html-reporter をインストールする
  5. gradle-node-plugin にするか task ...(type: Exec) {...} にするか?
  6. package.json に build 用の npm scripts を追加する
  7. build.gradle に npmBuild タスクを定義する
  8. npm run build を実行する時は webpack でソースマップを出力せず、uglify する
    1. Windows で NODE_ENV 環境変数で webpack の設定を切り替えられるようにするために cross-env モジュールをインストールする
    2. uglifyjs-webpack-plugin をインストールする
    3. webpack.config.js を変更する
    4. package.json を変更する
    5. 動作確認
  9. 次回は。。。

手順

目標

以下のことができるようにしてみたいと思います。

  • build.gradle に npmTest, npmBuild というタスクを作成する。
  • npmTest は npm test、npmBuild は npm run build(新規作成する)に関連させる。
  • npmTest → npmBuild → processResources → ... の順に実行する。
  • npm run springboot を実行する時は webpack でソースマップを出力し、かつ uglify しない。
  • npm run build を実行する時は webpack でソースマップを出力せず、uglify する。

gradle から npm scripts を実行するのに gradle-node-plugin を使用する

gradle から npm scripts を実行するのに srs/gradle-node-plugin というプラグインがあるとのことなので、このプラグインを使用して npm test を実行してみます。

build.gradle を以下のように変更します。

buildscript {
    ext {
        springBootVersion = '1.5.9.RELEASE'
    }
    repositories {
        mavenCentral()
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        // for Error Prone ( http://errorprone.info/ )
        classpath("net.ltgt.gradle:gradle-errorprone-plugin:0.0.13")
        classpath("com.moowork.gradle:gradle-node-plugin:1.2.0")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'groovy'
apply plugin: 'net.ltgt.errorprone'
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'com.moowork.node'

..........

task npmTest(type: NpmTask) {
    args = ["test"]
}
processResources.dependsOn npmTest

..........
  • buildscript に classpath("com.moowork.gradle:gradle-node-plugin:1.2.0") を追加します。
  • apply plugin: 'com.moowork.node' を追加します。
  • task npmTest(type: NpmTask) { ... } を追加します。
  • processResources.dependsOn npmTest を追加します。

clean タスク → build タスクの順に実行してみます。現在 Jest のテストは全て成功する状態です。

f:id:ksby:20180113012537p:plain .....(長いので途中は省略します)..... f:id:ksby:20180113012951p:plain

npmTest タスクは正常終了し、"BUILD SUCCESSFUL" も出力されました。ただし、

  • テストファイル1つに付き、PASS の文字が付いた行が出力されます。出来れば Java のテストのように成功時は何も出力されないようにしたいです。
  • Jest のテストに console.log(....); を書いているものがあるのですが、コンソールにログが大量に出力されました。gradle-node-plugin や Gradle の Webサイトを見てみたのですが、ログを抑制する方法が分かりません。。。

gradle から npm scripts を実行するのに task ...(type: Exec) {...} を使用する

cmd.exe で npm コマンドを実行して標準出力/エラー出力を NUL へ送ればコンソールにログが出力されないはずなので、task ...(type: Exec) {...} でタスクを定義してみます。

build.gradle を以下のように変更します。

//task npmTest(type: NpmTask) {
//    args = ["test"]
//}
task npmTest(type: Exec) {
    commandLine "cmd", "/c", "npm test >NUL 2>&1"
}
processResources.dependsOn npmTest
  • task npmTest(type: NpmTask) { ... }コメントアウトします。
  • task npmTest(type: Exec) { ... } を追加します。>NUL 2>&1 を付けて npm test の標準出力、エラー出力を一切出ないようにします。

clean タスク → build タスクの順に実行してみます。

f:id:ksby:20180119010456p:plain

npmTest タスクは正常終了し、"BUILD SUCCESSFUL" も出力されました。余計なログも出力されません。個人的にはこの出力結果の方が理想です。

テストが途中で失敗した時にそこで止まって先のタスクに進まないのかが気になるので試してみます。

src/test/assets/tests/lib/util/ZipcloudApiHelper.test.js の以下の点を変更します。

    // zipcloudApiHeler.search の dataType: "jsonp" を削除しないと下のテストは成功しないので
    // コメントアウトしておく
    describe("jquery-mockjax でサーバ側をモックにして $.ajax をテストする", () => {

        afterEach(() => {
            mockjax.clear();
        });
  • describe("jquery-mockjax でサーバ側をモックにして $.ajax をテストする", () => { ... } の前後の /**/ を削除し、コメントアウトを解除します。これで Jest のテストで失敗するものが出ます。

clean タスク → build タスクの順に実行してみると、npmTest タスクでエラーが出て止まりました。

f:id:ksby:20180120004638p:plain

ただしエラーがどこで出たのかレポートファイルで確認しようと思ったのですが、coverage のレポートしか出ていませんでした。Jest のページを見ましたが、テスト結果のレポートを出力させる方法がよく分かりません。。。

f:id:ksby:20180120010349p:plain

jest-html-reporter をインストールして試してみる

レポートファイルを生成する方法を調べてみたところ、jest-html-reporter というモジュールを見つけました。インストールして試してみます。

npm install --save-dev jest-html-reporter コマンドを実行してインストールします。

f:id:ksby:20180120012142p:plain

jest.config.json を以下のように変更します。

{
  "coverageDirectory": "build/reports/jest",
  "moduleDirectories": [
    "node_modules",
    "src/main/assets/js"
  ],
  "testResultsProcessor": "./node_modules/jest-html-reporter"
}
  • "testResultsProcessor": "./node_modules/jest-html-reporter" を追加します。

jest-html-reporter の設定は package.json に記述するようなので、package.json を以下のように変更します。

  "license": "ISC",
  "jest-html-reporter": {
    "outputPath": "build/reports/jest/jest-html-reporter.html",
    "includeFailureMsg": true
  },
  "dependencies": {
  • "jest-html-reporter": { ... } を追加します。

clean タスク → build タスクの順に実行してみます。先程と同様のエラーが出た後、build/reports/jest の下に jest-html-reporter.html が生成されていました。

f:id:ksby:20180120075554p:plain

jest-html-reporter.html をブラウザで表示させると、失敗したテストとエラーメッセージが表示されていました。

f:id:ksby:20180120075708p:plain

必要な情報が出ていて申し分がないので、jest-html-reporter をこのまま使用することにします。

gradle-node-plugin にするか task ...(type: Exec) {...} にするか?

環境依存しないと思われる gradle-node-plugin の方が良いとは思うのですが、task ...(type: Exec) {...} の方が不要な出力が出ず動作が好みなので、今回はこちらの方法を使用することにします。

package.json に build 用の npm scripts を追加する

package.jsonnpm run build で実行する npm scripts を定義します。

  "scripts": {
    "test": "jest --config=jest.config.json --coverage",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "clean:cssjs-dir": "rimraf src/main/resources/static/{css,js}/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons copy:jquery-ui-css",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "copy:jquery-ui-css": "cpx node_modules/jquery-ui/themes/base/**/* src/main/resources/static/vendor/jquery-ui/css",
    "postcss:build": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "webpack:build": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "browser-sync:springboot": "browser-sync start --config bs-springboot-config.js",
    "server": "run-p postcss:watch webpack:watch browser-sync:start",
    "springboot": "run-p postcss:watch webpack:watch browser-sync:springboot",
    "build": "run-s clean:cssjs-dir postcss:build webpack:build"
  },
  • 以下の4行を追加します。
    • "clean:cssjs-dir": "rimraf src/main/resources/static/{css,js}/*"
    • "postcss:build": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css"
    • "webpack:build": "webpack"
    • "build": "run-s clean:cssjs-dir postcss:build webpack:build"

コマンドプロンプトから npm run build を実行して正常に動作することを確認します。

f:id:ksby:20180220004635p:plain

build.gradle に npmBuild タスクを定義する

build.gradle を以下のように変更します。

..........

//task npmTest(type: NpmTask) {
//    args = ["test"]
//}
task npmTest(type: Exec) {
    commandLine "cmd", "/c", "npm test >NUL 2>&1"
}
task npmBuild(type: Exec) {
    commandLine "cmd", "/c", "npm run build >NUL 2>&1"
}
npmBuild.dependsOn npmTest
processResources.dependsOn npmBuild

repositories {
    mavenCentral()
}

..........
  • task npmBuild(type: Exec) { ... } を追加します。
  • npmBuild.dependsOn npmTestprocessResources.dependsOn npmBuild を追加します。

これで npmTest → npmBuild → processResources → ... の順にタスクが実行されるはずです。clean タスク → build タスクの順に実行してみます。

f:id:ksby:20180220005756p:plain

全てのタスクが正常終了し、"BUILD SUCCESSFUL" が出力されました。問題なさそうです。

npm run build を実行する時は webpack でソースマップを出力せず、uglify する

Windows で NODE_ENV 環境変数で webpack の設定を切り替えられるようにするために cross-env モジュールをインストールする

Spring Boot の spring.profiles.active のように webpack でも環境変数で設定を切り替えたい場合 NODE_ENV を使用しますが、npm scripts を "build": "NODE_ENV=product run-s clean:cssjs-dir postcss:build webpack:build" のように NODE_ENV=product を付けても Windows 環境では以下の画像のようにエラーになります。

f:id:ksby:20180221003011p:plain

エラーにならない方法を調べたところ、How can I set NODE_ENV=production on Windows?cross-env モジュールを使う方法が書かれていました。

npm install --save-dev cross-env コマンドを実行してインストールします。

f:id:ksby:20180221003701p:plain

uglifyjs-webpack-plugin をインストールする

webpack で js ファイルを uglify するには uglifyjs-webpack-plugin を使えばいいようです。npm install --save-dev uglifyjs-webpack-plugin コマンドを実行してインストールします。

f:id:ksby:20180221004220p:plain

webpack.config.js を変更する

GoogleChromeLabs/webpack-training-project を参考に、NODE_ENV=product が設定されている時だけ uglifyjs-webpack-plugin が適用されるようにします。ソースマップは uglifyjs-webpack-plugin が適用されると webpack.config.js に devtool: "inline-source-map" が記述されていても出力されなくなります。

const webpack = require("webpack");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const isProduct = process.env.NODE_ENV === "product";

module.exports = {
    ..........
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ].concat(
        isProduct
            ? [
                new UglifyJsPlugin()
            ]
            : []
    ),
    devtool: "inline-source-map"
};
  • 以下の2行を追加します。
    • const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
    • const isProduct = process.env.NODE_ENV === "product";
  • plugins に .concat(isProduct ? [new UglifyJsPlugin()] : []) を追加します。

package.json を変更する

package.json を以下のように変更します。

  "scripts": {
    ..........
    "springboot": "run-p postcss:watch webpack:watch browser-sync:springboot",
    "build": "cross-env NODE_ENV=product run-s clean:cssjs-dir postcss:build webpack:build"
  },
  • npm scripts の build の先頭に cross-env NODE_ENV=product を追加します。

動作確認

コマンドプロンプトから npm run build コマンドを実行してみます。

f:id:ksby:20180221005823p:plain

エラーが出ずに終了し、webpack でバンドルした後の js ファイルが 100KB 前後とかなり小さくなりました(上にある画像キャプチャを見ると cross-env NODE_ENV=product を指定していない npm run build コマンドでは 750KB ~ 1MB 程度でした)。ただし uglify するためか処理時間が 18秒もかかりますね。

ちなみに webpack.config.js から devtool: "inline-source-map" を削除して npm run build コマンドを実行しても生成される js ファイルのサイズは変わりません。

f:id:ksby:20180221065908p:plain

clean タスク → build タスクを実行すると、こちらも問題なく終了しました。

f:id:ksby:20180221011627p:plain

webpack によりバンドルされた input01.js を開いてみると、確かに uglify されています。

f:id:ksby:20180221014005p:plain

次回は。。。

入力画面3以降の作成を進めます。

履歴

2018/02/21
初版発行。