IntelliJ IDEA を 2017.2.5 → 2017.2.6 へ、Git for Windows を 2.14.2(2) → 2.15.0 へバージョンアップ
IntelliJ IDEA を 2017.2.5 → 2017.2.6 へバージョンアップする
IntelliJ IDEA の 2017.2.6 がリリースされたのでバージョンアップします。
- IntelliJ IDEA 2017.2.6 Release Notes
https://confluence.jetbrains.com/display/IDEADEV/IntelliJ+IDEA+2017.2.6+Release+Notes
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
IntelliJ IDEA のメインメニューから「Help」-「Check for Updates...」を選択します。
「IDE and Plugin Updates」ダイアログが表示されます。左下に「Update and Restart」ボタンが表示されていますので、「Update and Restart」ボタンをクリックします。
Plugin の update も表示されました。「Error-prone Compiler Integration」は後でアンインストールするので(Error-prone は gradle の build タスクを実行する時だけ動けばよさそうなので)、これだけチェックを外して「Update and Restart」ボタンをクリックします。
Patch がダウンロードされて IntelliJ IDEA が再起動します。
IntelliJ IDEA が起動すると画面下部に「Indexing…」のメッセージが表示されますので、終了するまで待機します。
IntelliJ IDEA のメインメニューから「Help」-「About」を選択し、2017.2.6 へバージョンアップされていることを確認します。
Gradle Tool Window のツリーを見ると「Tasks」の下に「other」しかない状態になっているので、左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run 'All Tests' with Coverage」を選択し、テストが全て成功することを確認します。
Git for Windows を 2.14.2(2) → 2.15.0 へバージョンアップする
Git for Windows の 2.15.0 がリリースされていたのでバージョンアップします。
https://git-for-windows.github.io/ の「Download」ボタンをクリックして Git-2.15.0-64-bit.exe をダウンロードします。
Git-2.15.0-64-bit.exe を実行します。
「Git 2.15.0 Setup」ダイアログが表示されます。[Next >]ボタンをクリックします。
「Select Components」画面が表示されます。「Git LFS(Large File Support)」だけチェックした状態で [Next >]ボタンをクリックします。
「Adjusting your PATH environment」画面が表示されます。中央の「Use Git from the Windows Command Prompt」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Choosing HTTPS transport backend」画面が表示されます。「Use the OpenSSL library」が選択されていることを確認後、[Next >]ボタンをクリックします。
「Configuring the line ending conversions」画面が表示されます。一番上の「Checkout Windows-style, commit Unix-style line endings」が選択されていることを確認した後、[Next >]ボタンをクリックします。
「Configuring the terminal emulator to use with Git Bash」画面が表示されます。「Use Windows'default console window」が選択されていることを確認した後、[Next >]ボタンをクリックします。
「Configuring extra options」画面が表示されます。「Enable file system caching」だけがチェックされていることを確認した後、[Install]ボタンをクリックします。
インストールが完了すると「Completing the Git Setup Wizard」のメッセージが表示された画面が表示されます。中央の「View Release Notes」のチェックを外した後、「Finish」ボタンをクリックしてインストーラーを終了します。
コマンドプロンプトを起動して
git --version
を実行し、git のバージョンがgit version 2.15.0.windows.1
になっていることを確認します。git-cmd.exe を起動して日本語の表示・入力が問題ないかを確認します。
特に問題はないようですので、2.15.0 で作業を進めたいと思います。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その35 )( Geb でテストを作成する2 )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その34 )( Geb でテストを作成する ) の続きです。
参照したサイト・書籍
目次
手順
HtmlUnit でテストが失敗した原因を取り除いてみる
前回 HtmlUnit でテストを実行したら java.lang.NoSuchMethodError: com.gargoylesoftware.htmlunit.html.DomElement.getScriptableObject()Ljava/lang/Object;
のエラーが出て失敗しました。
ライブラリの依存関係に問題がある気がします。gradlew dependencies
コマンドを実行してみたところ、以下の内容が出力されました。
+--- org.seleniumhq.selenium:htmlunit-driver:2.27 | +--- org.seleniumhq.selenium:selenium-api:3.4.0 -> 3.6.0 | +--- org.seleniumhq.selenium:selenium-support:3.4.0 -> 3.6.0 | | +--- org.seleniumhq.selenium:selenium-api:3.6.0 | | +--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*) | | +--- net.bytebuddy:byte-buddy:1.7.5 | | +--- org.apache.commons:commons-exec:1.3 | | +--- commons-codec:commons-codec:1.10 | | +--- commons-logging:commons-logging:1.2 | | +--- com.google.code.gson:gson:2.8.0 -> 2.8.1 | | +--- com.google.guava:guava:23.0 -> 22.0 (*) | | +--- org.apache.httpcomponents:httpclient:4.5.3 (*) | | +--- org.apache.httpcomponents:httpcore:4.4.6 | | +--- net.java.dev.jna:jna:4.1.0 -> 4.2.2 | | \--- net.java.dev.jna:jna-platform:4.1.0 (*) | \--- net.sourceforge.htmlunit:htmlunit:2.27 -> 2.21 | +--- xalan:xalan:2.7.2 | | \--- xalan:serializer:2.7.2 | +--- org.apache.commons:commons-lang3:3.4 -> 3.5 | +--- org.apache.httpcomponents:httpclient:4.5.2 -> 4.5.3 (*) | +--- org.apache.httpcomponents:httpmime:4.5.2 -> 4.5.3 | | \--- org.apache.httpcomponents:httpclient:4.5.3 (*) | +--- commons-codec:commons-codec:1.10 | +--- net.sourceforge.htmlunit:htmlunit-core-js:2.17 | +--- net.sourceforge.htmlunit:neko-htmlunit:2.21 | | \--- xerces:xercesImpl:2.11.0 | | \--- xml-apis:xml-apis:1.4.01 | +--- net.sourceforge.cssparser:cssparser:0.9.18 | | \--- org.w3c.css:sac:1.3 | +--- commons-io:commons-io:2.4 -> 2.5 | +--- commons-logging:commons-logging:1.2 | \--- org.eclipse.jetty.websocket:websocket-client:9.2.15.v20160210 -> 9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 | | \--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-client:9.4.6.v20170531 | | +--- org.eclipse.jetty:jetty-http:9.4.6.v20170531 | | | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | \--- org.eclipse.jetty.websocket:websocket-common:9.4.6.v20170531 | +--- org.eclipse.jetty.websocket:websocket-api:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) +--- org.seleniumhq.selenium:selenium-support:3.6.0 (*) +--- org.seleniumhq.selenium:selenium-api:3.6.0 \--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*)
net.sourceforge.htmlunit:htmlunit:2.27 -> 2.21
とバージョンダウンされているモジュールがありました。net.sourceforge.htmlunit:htmlunit
の 2.27 が使用されるよう build.gradle を以下のように変更します。
dependencies { .......... // for Geb + Spock .......... testCompile("org.seleniumhq.selenium:htmlunit-driver:2.27") testCompile("net.sourceforge.htmlunit:htmlunit:2.27") .......... }
testCompile("net.sourceforge.htmlunit:htmlunit:2.27")
を追加します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
再び gradlew dependencies
コマンドを実行すると、今度は net.sourceforge.htmlunit:htmlunit:2.27
と出力されました。
+--- org.seleniumhq.selenium:htmlunit-driver:2.27 | +--- org.seleniumhq.selenium:selenium-api:3.4.0 -> 3.6.0 | +--- org.seleniumhq.selenium:selenium-support:3.4.0 -> 3.6.0 | | +--- org.seleniumhq.selenium:selenium-api:3.6.0 | | +--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*) | | +--- net.bytebuddy:byte-buddy:1.7.5 | | +--- org.apache.commons:commons-exec:1.3 | | +--- commons-codec:commons-codec:1.10 | | +--- commons-logging:commons-logging:1.2 | | +--- com.google.code.gson:gson:2.8.0 -> 2.8.1 | | +--- com.google.guava:guava:23.0 -> 22.0 (*) | | +--- org.apache.httpcomponents:httpclient:4.5.3 (*) | | +--- org.apache.httpcomponents:httpcore:4.4.6 | | +--- net.java.dev.jna:jna:4.1.0 -> 4.2.2 | | \--- net.java.dev.jna:jna-platform:4.1.0 (*) | \--- net.sourceforge.htmlunit:htmlunit:2.27 | +--- xalan:xalan:2.7.2 | | \--- xalan:serializer:2.7.2 | +--- org.apache.commons:commons-lang3:3.5 | +--- org.apache.httpcomponents:httpmime:4.5.3 | | \--- org.apache.httpcomponents:httpclient:4.5.3 (*) | +--- net.sourceforge.htmlunit:htmlunit-core-js:2.27 | +--- net.sourceforge.htmlunit:neko-htmlunit:2.27 | | \--- xerces:xercesImpl:2.11.0 | | \--- xml-apis:xml-apis:1.4.01 | +--- net.sourceforge.cssparser:cssparser:0.9.23 | | \--- org.w3c.css:sac:1.3 | +--- commons-io:commons-io:2.5 | +--- commons-logging:commons-logging:1.2 | \--- org.eclipse.jetty.websocket:websocket-client:9.4.5.v20170502 -> 9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 | | \--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-client:9.4.6.v20170531 | | +--- org.eclipse.jetty:jetty-http:9.4.6.v20170531 | | | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) | \--- org.eclipse.jetty.websocket:websocket-common:9.4.6.v20170531 | +--- org.eclipse.jetty.websocket:websocket-api:9.4.6.v20170531 | +--- org.eclipse.jetty:jetty-util:9.4.6.v20170531 | \--- org.eclipse.jetty:jetty-io:9.4.6.v20170531 (*) +--- net.sourceforge.htmlunit:htmlunit:2.27 (*) +--- org.seleniumhq.selenium:selenium-support:3.6.0 (*) +--- org.seleniumhq.selenium:selenium-api:3.6.0 \--- org.seleniumhq.selenium:selenium-remote-driver:3.6.0 (*)
再度 HtmlUnit でテストを実行してみると、今度は成功するテストが出てきました。ただし、まだ一部のテストが失敗しています。失敗したテストを見てみると、
こちらは autocomplete のテストで失敗しており、5秒経過しても指定した selector が見つからないようです。
こちらはエラーメッセージが表示されていないようです。
いろいろ試してみると、後者のエラーメッセージが出ない方はテストを以下のように変更すると解消されました。
when: "電話番号と郵便番号を入力する" $("#inquiryInput02Form").tel1 = tel1 << Keys.TAB $("#inquiryInput02Form").tel2 = tel2 << Keys.TAB $("#inquiryInput02Form").tel3 = tel3 << Keys.TAB $("#inquiryInput02Form").email = email << Keys.TAB
↓↓↓
when: "電話番号と郵便番号を入力する" $("#inquiryInput02Form").tel1 = tel1 $("#tel2") << "" $("#inquiryInput02Form").tel2 = tel2 $("#tel3") << "" $("#inquiryInput02Form").tel3 = tel3 $("#email") << "" $("#inquiryInput02Form").email = email $("#tel1") << ""
テストを実行すると autocomplete 以外のテストが成功します。
HtmlUnit だと Keys.TAB を送信しても次の入力項目にフォーカスが移動せず、フォーカスを移動したい入力項目に << ""
を送信しないといけないようです。
ということは autocomplete のテストを以下のように修正すると、
when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" << Keys.TAB
↓↓↓
when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" $("#address") << ""
今度は全てのテストが成功しました。
HtmlUnit は Geb のテスト対象ブラウザから外す
$("#inquiryInput02Form").tel1 = tel1 << Keys.TAB
のように記述して TAB キーを押したら次の入力項目にフォーカスが移動できた方が個人的には分かりやすく、かつテストが書きやすいのと、Firefox や Chrome の headless が十分使いやすいので、HtmlUnit は Geb のテスト対象ブラウザから外すことにします。
build.gradle を以下のように変更します。
dependencies { .......... // for Geb + Spock testCompile("org.gebish:geb-spock:2.0") testCompile("org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-firefox-driver:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-support:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-api:${seleniumVersion}") testCompile("org.seleniumhq.selenium:selenium-remote-driver:${seleniumVersion}") } .......... // for Geb + Spock Integration Test def drivers = ["chrome", "firefox"] drivers.each { driver -> task "${driver}Test"(type: Test) { // 前回実行時以降に何も更新されていなくても必ず実行する outputs.upToDateWhen { false } systemProperty "geb.env", driver exclude "ksbysample/**" } } task gebTest { dependsOn drivers.collect { tasks["${it}Test"] } enabled = false }
- 以下の2行を削除します。
testCompile("org.seleniumhq.selenium:htmlunit-driver:2.27")
testCompile("net.sourceforge.htmlunit:htmlunit:2.27")
def drivers = ["chrome", "firefox", "htmlunit"]
→def drivers = ["chrome", "firefox"]
に変更します。
変更後、Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
src/test/resources/GebConfig.groovy を以下のように変更します。
environments { chrome { driver = { ChromeOptions chromeOptions = new ChromeOptions() chromeOptions.setHeadless(true) new ChromeDriver(chromeOptions) } } firefox { driver = { FirefoxOptions firefoxOptions = new FirefoxOptions() firefoxOptions.setHeadless(true) new FirefoxDriver(firefoxOptions) } } }
- environments から
htmlunit { new HtmlUnitDriver(true) }
を削除します。
gebTest タスクを実行して "BUILD SUCCESSFUL" が表示されることを確認します。
Geb でいろいろ試してみる
画面の外にボタンがあるとクリックできない?
Geb について書かれた記事を見ていると、ボタンがブラウザの Window 内に表示されていない場合クリックできないと書かれたものを何回か見かけので、確認してみます。
まずは <br>
タグを大量に入れて、「次へ」ボタンを画面の外に追い出します。
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に以下のテストを記述します。
def "次へボタンが画面の外にあるとクリックできない?" () { setup: "入力画面1を表示してデータを入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" // 念のため、フォーカスを「お名前(漢字)」へ移動する $("#lastname") << "" expect: "次へボタンをクリックする" form.btnNext.click(InquiryInput02Page) }
テストを実行すると問題なく成功しますね。。。
src/test/resources/GebConfig.groovy を以下のように変更して headless モードを解除し、
driver = { FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) new FirefoxDriver(firefoxOptions) }
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に Thread.sleep(3000)
を追加して、
def "次へボタンが画面の外にあるとクリックできない?" () { setup: "入力画面1を表示してデータを入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" // 念のため、フォーカスを「お名前(漢字)」へ移動する $("#lastname") << "" Thread.sleep(3000) expect: "次へボタンをクリックする" form.btnNext.click(InquiryInput02Page) }
テストを実行してブラウザの動きを見てみたところ、form.btnNext.click(InquiryInput02Page)
の処理を実行する時に「次へ」ボタンへフォーカスが移動して画面の見える位置に移動していました。Firefox Quantum だと問題なく動作するようです。
src/test/resources/GebConfig.groovy を以下のように変更してブラウザを Chrome に変更しても、
driver = { // FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) // new FirefoxDriver(firefoxOptions) ChromeOptions chromeOptions = new ChromeOptions() chromeOptions.setHeadless(true) new ChromeDriver(chromeOptions) }
テストは成功しました。Chrome も問題ないようです。
gradle から gebTest タスクを実行しても成功しました。
画面の外にボタンがあっても問題なくクリックできるようです。変更したソースは元に戻します。
maxlength を超える文字列は入力できない
Geb で値をセットした時に maxlength を超える文字列がセットされるのか気になったので、確認してみます。
src/test/groovy/geb/gebspec/inquiry/InquiryTestSpec.groovy に以下のテストを記述して、
def "maxlength を超えた文字列は入力できない?"() { setup: to InquiryInput01Page // lastname の maxlength は 20 なので 21文字セットしてみる $("#lastname").value("あ" * 20 + "い") expect: $("#lastname") == "あ" * 20 + "い" }
テストを実行すると失敗しました。21文字目の "い" がセットされていませんでした。
maxlength を超える文字列はセットできないようです。
FormModule クラスの setValueList メソッドを値セット+Tab キークリックするよう変更する
src/test/groovy/geb/module/FormModule.groovy の setValueList メソッドは今は以下の実装ですが、
void setValueList(valueList) { valueList.each { $(it.key).value(it.value) } }
空のデータをセットしてエラーメッセージを表示する以下のテストを実行した時に、
def "テスト"() { setup: to InquiryInput01Page form.setValueList(initialValueList) Thread.sleep(10000) expect: true }
年齢に空文字列をセットしたのに、フォーカスが移動していないのでエラーメッセージが表示されていないことに気づきました。
src/test/groovy/geb/module/FormModule.groovy を以下のよう変更し、値をセットした後にTABキーをクリックして blur イベントが発生するようにします。
void setValueList(valueList) { valueList.each { $(it.key).value(it.value) $(it.key) << Keys.TAB } }
$(it.key) << Keys.TAB
を追加します。
これでさっきのテストを実行すると、今度は年齢に空文字列をセットしてTABキーがクリックされるのでエラーメッセージが表示されます。
次回は。。。
Geb を使用したテストの作り方が簡単ながらも分かったので一旦ここで終了します。入力画面3に進む前に少し迷走する予定です。。。
履歴
2017/11/24
初版発行。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その34 )( Geb でテストを作成する )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その33 )( ESLint を導入する ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Geb で入力画面1、入力画面2のテストを作成してみます。
- Form に値を一括セット/検証する方法を調べていたら結構時間がかかってしまいました。
参照したサイト・書籍
The Book Of Geb
http://www.gebish.org/manual/current/Gebチートシート
https://qiita.com/itagakishintaro/items/1fa06904bd0a6de73ee2Geb Advent Calendar 2016
https://qiita.com/advent-calendar/2016/gebGeb 自分用メモ
http://bufferings.hatenablog.com/entry/2015/06/11/010321Web画面自動テストフレームワーク「Geb」の紹介
http://lab.astamuse.co.jp/entry/geb_test_01Can selenium handle autocomplete?
https://stackoverflow.com/questions/663034/can-selenium-handle-autocomplete
目次
- Firefox Quantum がインストールされていました
- FormModule クラスを作成する
- 入力画面1の InquiryInput01Page クラスを変更し、テストデータを用意する
- 入力画面2の InquiryInput02Page クラスを作成し、テストデータを用意する
- Geb のテストを作成する
- 続きます。。。
手順
Firefox Quantum がインストールされていました
Firefox を再起動したら Firefox Quantum になっていました。そういえば正式リリース日でしたね(2017/11/15 に書いています)。既存の Firefox とは別にインストールする必要があるのかな、と思っていたら自動でアップデートされていました。
サンプルで書いていた Geb のテストを試したら問題なく動くようなので、このまま続けます。
FormModule クラスを作成する
画面共通のボタンを定義したり、Form に値を一括セット/検証するメソッドを作成したいので、Geb の Module を継承したクラスを作成します。
src/test/groovy/geb の下に module パッケージを作成します。src/test/groovy/geb/module の下に FormModule.groovy を新規作成し、以下の内容を記述します。
package geb.module import geb.Module import org.openqa.selenium.WebElement /** * フォーム共通の content や、テストに使用するメソッドを定義するクラス */ class FormModule extends Module { static content = { btnBack { $(".js-btn-back") } btnNext { $(".js-btn-next") } } /** * Form の入力項目に値を一括セットする * valueList は以下の形式の Map である * <pre>{@code * static initialValueList = [ * "#lastname" : "", * "#firstname" : "", * "#lastkana" : "", * "#firstkana" : "", * "input[name='sex']": null, * "#age" : "", * "#job" : "" * ] *}</pre> * * @param valueList セットするセレクタと値を記述した Map */ void setValueList(valueList) { valueList.each { $(it.key).value(it.value) } } /** * セレクタに値がセットされているかを検証する * このメソッドは Spock の then, expect で使用する想定である * * @param valueList 検証するセレクタと値を記述した Map * @return true 固定 */ boolean assertValueList(valueList) { valueList.each { key, value -> if ($(key).first().attr("type") == "radio") { WebElement element = $(key).allElements().find { it.selected } if (element == null) { assert null == value } else { assert element.getAttribute("value") == value } } else { assert $(key).value() == value } } true } }
入力画面1の InquiryInput01Page クラスを変更し、テストデータを用意する
入力画面1用に作成していた InquiryInput01Page クラスで FormModule を使用するようにし、かつテストで使用するデータも記述します。src/test/groovy/geb/page/inquiry/InquiryInput01Page.groovy を以下のように変更します。
package geb.page.inquiry import geb.Page import geb.module.FormModule class InquiryInput01Page extends Page { static url = "/inquiry/input/01" static at = { title == "入力フォーム - 入力画面1" } static content = { form { module FormModule } } static initialValueList = [ "#lastname" : "", "#firstname" : "", "#lastkana" : "", "#firstkana" : "", "input[name='sex']": null, "#age" : "", "#job" : "" ] static maxLengthValueList = [ "#lastname" : "あ" * 20, "#firstname": "あ" * 20, "#lastkana" : "あ" * 20, "#firstkana": "あ" * 20, "#age" : "9" * 3, ] }
static content = { ... }
からbtnNext { $(".js-btn-next") }
を削除し、form { module FormModule }
を追加します。static initialValueList = [ ... ]
を追加します。static maxLengthValueList = [ ... ]
を追加します。
入力画面2の InquiryInput02Page クラスを作成し、テストデータを用意する
入力画面2用の InquiryInput02Page クラスを作成します。テストで使用するデータも記述します。
src/test/groovy/geb/page/inquiry の下に InquiryInput02Page.groovy を新規作成し、以下の内容を記述します。
package geb.page.inquiry import geb.Page import geb.module.FormModule class InquiryInput02Page extends Page { static url = "/inquiry/input/01" static at = { title == "入力フォーム - 入力画面2" } static content = { form { module FormModule } } static initialValueList = [ "#zipcode1": "", "#zipcode2": "", "#address" : "", "#tel1" : "", "#tel2" : "", "#tel3" : "", "#email" : "", ] static maxLengthValueList = [ "#zipcode1": "x" * 3, "#zipcode2": "x" * 4, "#address" : "あ" * 256, "#tel1" : "9" * 5, "#tel2" : "9" * 4, "#tel3" : "9" * 4, "#email" : "x" * 256, ] }
Geb のテストを作成する
入力画面1、入力画面2でできるテストを作成してみます。
src/test/groovy/geb/gebspec の下に inquiry パッケージを作成した後、inquiry パッケージの下に InquiryTestSpec.groovy を新規作成して以下の内容を記述します。
package geb.gebspec.inquiry import geb.page.inquiry.InquiryInput01Page import geb.page.inquiry.InquiryInput02Page import geb.spock.GebSpec import org.openqa.selenium.Keys import org.openqa.selenium.WebElement import spock.lang.Unroll class InquiryTestSpec extends GebSpec { def "入力画面1の画面初期表示時に想定している値がセットされている"() { setup: "入力画面1を表示する" to InquiryInput01Page expect: "初期値が表示されている" form.assertValueList(initialValueList) } def "入力画面1の各入力項目に最大文字数を入力できる"() { setup: "入力画面1を表示し入力項目に最大文字数の文字を入力する" to InquiryInput01Page form.setValueList(maxLengthValueList) expect: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) } def "入力画面1に入力後、入力画面2へ遷移→入力画面1へ戻ると入力した値が表示される"() { given: "入力画面1を表示する" to InquiryInput01Page when: "最大文字数の文字を入力して次へボタンをクリックする" form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" form.btnNext.click(InquiryInput02Page) then: "入力画面2へ遷移し初期値が表示されている" form.assertValueList(initialValueList) and: "戻るボタンをクリックする" form.btnBack.click(InquiryInput01Page) then: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) $("#inquiryInput01Form").sex == "1" } def "入力画面2の各入力項目に最大文字数を入力できる"() { setup: "入力画面1を表示して最大文字数の文字を入力してから次へボタンをクリックする" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" form.btnNext.click(InquiryInput02Page) and: "入力画面2で最大文字数の文字を入力する" form.setValueList(maxLengthValueList) expect: "入力した最大文字数の文字が入力されている" form.assertValueList(maxLengthValueList) } def "入力画面2で郵便番号を入力してautocompleteで表示された住所を選択する"() { given: "入力画面1から入力画面2へ遷移する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" form.btnNext.click(InquiryInput02Page) when: "郵便番号を入力する" $("#inquiryInput02Form").zipcode1 = "100" $("#inquiryInput02Form").zipcode2 = "0005" << Keys.TAB and: "autocomplete のドロップダウンメニューが表示されたら最初の選択肢を選択する" waitFor(5) { $(".ui-autocomplete .ui-menu-item") } List<WebElement> elementList = $(".ui-autocomplete .ui-menu-item > div").allElements() elementList.first().click() then: "住所にクリックした選択肢がセットされている" $("#inquiryInput02Form").address == "東京都千代田区丸の内" } @Unroll def "入力画面2の電話番号とメールアドレスの組み合わせテスト(#tel1,#tel2,#tel3,#email)"() { given: "入力画面1から入力画面2へ遷移する" to InquiryInput01Page form.setValueList(maxLengthValueList) $("#inquiryInput01Form").sex = "1" form.btnNext.click(InquiryInput02Page) when: "電話番号と郵便番号を入力する" $("#inquiryInput02Form").tel1 = tel1 << Keys.TAB $("#inquiryInput02Form").tel2 = tel2 << Keys.TAB $("#inquiryInput02Form").tel3 = tel3 << Keys.TAB $("#inquiryInput02Form").email = email << Keys.TAB then: "エラーメッセージの表示状況をチェックする" $("#form-group-tel .js-errmsg").text() == telErrMsg $("#form-group-email .js-errmsg").text() == emailErrMsg where: tel1 | tel2 | tel3 | email || telErrMsg | emailErrMsg "03" | "1234" | "5678" | "tanaka@sample.co.jp" || "" | "" "03" | "1234" | "5678" | "" || "" | "" "" | "" | "" | "tanaka@sample.co.jp" || "" | "" "" | "" | "" | "" || "電話番号とメールアドレスのいずれか一方を入力してください" | "電話番号とメールアドレスのいずれか一方を入力してください" "3" | "1234" | "5678" | "" || "市外局番の先頭には 0 の数字を入力してください" | "" "03" | "123" | "5678" | "" || "市外局番+市内局番の組み合わせが数字6桁になるように入力してください" | "" "03" | "1234" | "567" | "" || "加入者番号には4桁の数字を入力してください" | "" } }
テストを書いてみて思ったのは、以下の点でした。
- InquiryInput01Page にも InquiryInput02Page にも
static content = { form { module FormModule } }
と書いており、テストはform.~
から書いて問題なく動作するのかな?と不思議に思っていましたが、to ...
やform.btnNext.click(...Page)
を呼び出して画面遷移した後の Page クラスの form を見てくれるようです。 - 値をセットするだけなら不要ですが、Javascript の blur イベントを発生させたい場合には
<< Keys.TAB
が必須でした。 - autocomplete のテストは時間がかかりましたが、それ以外は MockMvc と比較するとテストが書きやすいです。ただし実行に時間がかかるので、何でも Geb でテストを作成するという訳にはいかなそうです。
作成したテストを実行してみます。Tomcat を起動した後、テストを実行します。
テストは全て成功しました。最初のテストが 10数秒、それ以降のテストが2~4秒程度かかるようです。ブラウザを起動してテストをするので、やっぱり結構時間がかかりますね。
Headless Chrome に切り替えてみます。src/test/resources/GebConfig.groovy を以下のように変更します。
driver = { // FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) // new FirefoxDriver(firefoxOptions) ChromeOptions chromeOptions = new ChromeOptions() chromeOptions.setHeadless(true) new ChromeDriver(chromeOptions) }
テストを実行します。
Firefox headless モードの時と同様にテストは全て成功しました。Firefox headless モードと比較すると最初のテストは数秒速いですが、その後のテストが 0.5~2秒くらい遅い気がします。
最後に HtmlUnit に切り替えてみます。src/test/resources/GebConfig.groovy を以下のように変更します。
driver = { // FirefoxOptions firefoxOptions = new FirefoxOptions() // firefoxOptions.setHeadless(true) // new FirefoxDriver(firefoxOptions) new HtmlUnitDriver(true) }
テストを実行します。
なぜか全部失敗しましたね。。。 java.lang.NoSuchMethodError: com.gargoylesoftware.htmlunit.html.DomElement.getScriptableObject()Ljava/lang/Object;
というエラーが出ています。
続きます。。。
HtmlUnit で失敗する原因を調べます。また Geb でもう少しいろいろ試してみます。
履歴
2017/11/17
初版発行。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その33 )( ESLint を導入する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
ESLint - Pluggable JavaScript linter
https://eslint.org/愚直にESLintを導入した話
http://tech.mercari.com/entry/2017/07/31/170125eslint-config-airbnb-base
https://www.npmjs.com/package/eslint-config-airbnb-basewebpack から ESLint を使う設定
https://www.pupha.net/archives/3289/eslint-loader
https://www.npmjs.com/package/eslint-loaderESLintでReactとES2015の構文チェック(eslint-config-airbnb)
http://dackdive.hateblo.jp/entry/2016/05/13/094000ESLint - Rules
https://eslint.org/docs/rules/Can I use eslint-config-airbnb without eslint-plugin-react?
https://github.com/airbnb/javascript/issues/451ESLint dollar($) is not defined. (no-undef)
https://stackoverflow.com/questions/39510736/eslint-dollar-is-not-defined-no-undefAirbnb JavaScript Style Guide
https://github.com/airbnb/javascript
目次
- ESLint をインストールする
eslint --init
を実行する- ECMAScript 6 用の Rule を含まない
airbnb-base
→airbnb-base/legacy
に変更する - webpack から eslint が実行されるよう設定する
- 動作確認
- インデントと改行コードと文字列のクォーテーションを airbnb-base のものではなく独自ルールに変更する
- 修正を進める前に IntelliJ IDEA の ESLint の機能を有効にする
- 多めに出力されているメッセージに対応する
- jquery.autoKana.js をチェック対象から外す
- app.js を削除する
'$' is not defined
Unexpected unnamed function
All 'var' declarations must be at the top of the function scope
Expected a newline after '('
Strings must use doublequote
'errmsg' used outside of binding context
'event' is defined but never used
Missing semicolon
Unexpected use of 'location'
- 各ファイル毎の出力されているメッセージに対応する
- 最後に
手順
ESLint をインストールする
npm install --save-dev eslint
コマンドを実行してインストールします。
eslint --init
を実行する
git コマンドを実行するために使用している git-cmd.exe を起動してから ./node_modules/.bin/eslint --init
コマンドを実行します。CUI で4つ質問事項が表示されますので、下の画像の上の方に表示されている内容を回答します。
How would you like to configure ESLint?
-->Use a popular style guide
Which style guide do you want to follow?
-->Airbnb
Do you use React?
-->No
What format do you want your config file to be in?
-->JavaScript
https://eslint.org/docs/user-guide/getting-started に書かれていたコマンドが ./node_modules/.bin/eslint --init
だったので git-cmd.exe から実行しましたが、/
--> \
に変更してコマンドプロンプトから実行しても同じように動作します。
質問に回答すると npm で eslint-config-airbnb-base と eslint-plugin-import の package がインストールされて、package.json が以下のように変更され、
.......... "cssnano": "^3.10.0", "eslint": "^4.10.0", "eslint-config-airbnb-base": "^12.1.0", "eslint-plugin-import": "^2.8.0", "http-proxy-middleware": "^0.17.4", ..........
- 以下の2行が追加されていました。
"eslint-config-airbnb-base": "^12.1.0"
"eslint-plugin-import": "^2.8.0"
プロジェクトのルート直下に以下の内容の .eslintrc.js が生成されます。
module.exports = { "extends": "airbnb-base" };
ECMAScript 6 用の Rule を含まない airbnb-base
→ airbnb-base/legacy
に変更する
node_modules/eslint-config-airbnb-base/index.js は ECMAScript 6 用の Rule が記述されている './rules/es6'
が含まれていますが、
module.exports = { extends: [ './rules/best-practices', './rules/errors', './rules/node', './rules/style', './rules/variables', './rules/es6', './rules/imports', ].map(require.resolve), parserOptions: { ecmaVersion: 2017, sourceType: 'module', ecmaFeatures: { experimentalObjectRestSpread: true, }, }, rules: { strict: 'error', }, };
'./rules/es6'
を含まない node_modules/eslint-config-airbnb-base/legacy.js というファイルもあります。
module.exports = { extends: [ './rules/best-practices', './rules/errors', './rules/node', './rules/style', './rules/variables' ].map(require.resolve), env: { browser: true, node: true, amd: false, mocha: false, jasmine: false }, rules: { 'comma-dangle': ['error', 'never'], 'prefer-numeric-literals': 'off', 'no-restricted-properties': ['error', { object: 'arguments', property: 'callee', message: 'arguments.callee is deprecated', }, { property: '__defineGetter__', message: 'Please use Object.defineProperty instead.', }, { property: '__defineSetter__', message: 'Please use Object.defineProperty instead.', }], } };
今回 ECMAScript 6 は使用していないので、legacy.js を使用するよう .eslintrc.js を以下のように変更します。
module.exports = { "extends": "airbnb-base/legacy" };
airbnb-base
→airbnb-base/legacy
に変更します。
webpack から eslint が実行されるよう設定する
npm install --save-dev eslint-loader
コマンドを実行して eslint-loader をインストールします。
webpack.config.js を以下のように変更します。
module.exports = { .......... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader" } ] }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ] };
module: { ... }
を追加します。
動作確認
npm run spingboot
コマンドを実行して webpack を実行してみると、なんか大量にメッセージが出力されました。。。
改行コードとインデントと文字列のクォーテーションで結構メッセージが表示されているようなので、まずは eslint のルールを今 js ファイルを書いているルール(4インデント+CRLF+ダブルクォーテーション)に変更できないか調べてみます。
インデントと改行コードと文字列のクォーテーションを airbnb-base のものではなく独自ルールに変更する
カスタマイズは .eslintrc.js に "rules": { ... }
で記述すればできるようなので、.eslintrc.js を以下のように変更します。
module.exports = { "extends": "airbnb-base", "rules": { // this option sets a specific tab width for your code // http://eslint.org/docs/rules/indent indent: ['error', 4, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1, // MemberExpression: null, FunctionDeclaration: { parameters: 1, body: 1 }, FunctionExpression: { parameters: 1, body: 1 }, CallExpression: { arguments: 1 }, ArrayExpression: 1, ObjectExpression: 1, ImportDeclaration: 1, flatTernaryExpressions: false, ignoredNodes: ['JSXElement', 'JSXElement *'] }], // disallow mixed 'LF' and 'CRLF' as linebreaks // http://eslint.org/docs/rules/linebreak-style 'linebreak-style': ['error', 'windows'], // specify whether double or single quotes should be used quotes: ['error', 'double', { avoidEscape: true }] } };
- 改行コードとインデントと文字列のクォーテーションの設定は全て node_modules/eslint-config-airbnb-base/rules/style.js の中に書かれていたので、そこからコピーして設定を変更します。
- indent の設定は 2-space indent 以外にもいろいろ設定が入っていたので、コピーして
2
→4
のみ変更します。 linebreak-style
の設定をunix
→windows
に変更します。quotes
の設定をsingle
→double
に変更します。
再度 npm run spingboot
コマンドを実行すると改行コードとインデントと文字列のクォーテーションではメッセージが出なくなりました。
修正を進める前に IntelliJ IDEA の ESLint の機能を有効にする
IntelliJ IDEA には ESLint を利用してソースコードをチェックする機能があるので、それを有効にします。
メインメニューから「File」-「Settings...」を選択します。「Settings」ダイアログを表示されるので、画面左上の検索フィールドに "eslint" と入力します。
ESLint の設定画面が表示されるので、以下の画面のように設定します。
- 「Enable」をチェックします。
- 「Node interpreter」に "node.exe" のパスを設定します。
- 「Configuration file」はデフォルトの「Automatic search」を選択したままです。
- 「Additional rules directory」「Extra eslint options」は何も設定しません。
「OK」ボタンを押してダイアログを閉じた後 js のソースコードを表示すると、以下の画像のように ESLint のチェックに引っかかった行の右側に赤線が表示され、マウスオーバーすると ESLint のメッセージが表示されます。
またエディタ上で右クリックしてコンテキストメニューを表示した後「Analyze」-「Inspect Code...」を選択し、
「Specify Inspection Scope」ダイアログが表示されたらそのまま「OK」を押すと、
画面下部に Inspection の結果が表示されます。この中に「Code quality tools」という項目があり、
展開すると ESLint のメッセージと該当箇所が表示されます。
多めに出力されているメッセージに対応する
ざっと見てまだ多めに出ているメッセージから対応します。
jquery.autoKana.js をチェック対象から外す
jquery.autoKana.js がチェックされてメッセージが出ていましたので、チェック対象から外します。webpack.config.js を以下のように変更します。
module: { rules: [ { test: /\.js$/, exclude: [ /node_modules/, /jquery.autoKana.js$/ ], loader: "eslint-loader" } ] },
- exclude に
/jquery.autoKana.js$/
を追加します。
app.js を削除する
src/main/assets/js/app.js もチェックされていましたが、このファイルは最初に作成したサンプルでもう不要なので削除します。
app.js を削除した後、webpack.config.js を以下のように変更します。
module.exports = { entry: { "js/inquiry/input01": ["./src/main/assets/js/inquiry/input01.js"], "js/inquiry/input02": ["./src/main/assets/js/inquiry/input02.js"], "js/inquiry/input03": ["./src/main/assets/js/inquiry/input03.js"], "js/inquiry/confirm": ["./src/main/assets/js/inquiry/confirm.js"] },
"js/app": ["./src/main/assets/js/app.js"]
を削除します。
'$' is not defined
ESLint dollar($) is not defined. (no-undef) を見ると "jquery": true
を定義すればチェックされないようになるようです。.eslintrc.js を以下のように変更します。
module.exports = { "extends": "airbnb-base/legacy", "env": { "jquery": true }, ..........
"env": { "jquery": true }
を追加します。
Unexpected unnamed function
無名関数は多用するので、この Rule は無効にします。.eslintrc.js を以下のように変更します。
"rules": { // require function expressions to have a name // http://eslint.org/docs/rules/func-names 'func-names': 'off',
'func-names': 'off'
を追加します。
All 'var' declarations must be at the top of the function scope
変数宣言は関数スコープ内の最初に行うこと、というメッセージのようですが、エラーが出ている原因は require("vendor/autokana/jquery.autoKana.js");
や require("jquery-ui/ui/widgets/autocomplete.js");
を上に書いているので、その下の var で宣言している部分でチェックに引っかかっているためのようです。
var より require を上にまとめておきたいので、この Rule は無効にします。.eslintrc.js を以下のように変更します。
"rules": { // requires to declare all vars on top of their containing scope 'vars-on-top': 'off',
'vars-on-top': 'off'
を追加します。
Expected a newline after '('
https://eslint.org/docs/rules/function-paren-newline を見ると、(
の後に続けて引数を記述するのではなく、一旦改行をして次の行から書くこと、というメッセージのようです。なんか縦に長くなってソースが見にくくなる気しかしないので、これは無効にします。
"rules": { .......... // require function expressions to have a name // http://eslint.org/docs/rules/func-names 'func-names': 'off', // enforce consistent line breaks inside function parentheses // https://eslint.org/docs/rules/function-paren-newline 'function-paren-newline': 'off', // this option sets a specific tab width for your code // http://eslint.org/docs/rules/indent indent: ['error', 4, {
'function-paren-newline': 'off'
を追加します。
Strings must use doublequote
文字列のクォーテーションはダブルクォーテーションを使用するルールを指定しましたが、シングルクォーテーションになっているところがありました。こちらは全てダブルクォーテーションに修正します。
'errmsg' used outside of binding context
src/main/assets/js/inquiry/input02.js で errmsg 変数を block スコープ毎に var で宣言していたので引っかかっているようです。関数の最初で var で宣言するように修正します。
'event' is defined but never used
イベントハンドラの無名関数の引数に使用している/いないに関わらず event という引数を書いていたので、そこで引っかかっているようです。使用されていない event 引数は削除します。
Missing semicolon
単純に末尾のセミコロンのつけ忘れです。修正します。
Unexpected use of 'location'
node_modules/eslint-config-airbnb-base/rules/variables.js を見ると以下のように定義されており、
const restrictedGlobals = require('eslint-restricted-globals'); module.exports = { rules: { .......... // disallow specific globals 'no-restricted-globals': ['error', 'isFinite', 'isNaN'].concat(restrictedGlobals),
node_modules/eslint-restricted-globals/index.js には以下のように定義されていました。
module.exports = [ .......... 'length', 'location', 'locationbar',
location
をグローバル変数として使用すると no-restricted-globals
の Rule に引っかかりエラーになるようです。
ソースコードを修正して対応します。location
→ window.location
に変更します。window
は node_modules/eslint-restricted-globals/index.js に定義されておらず、グローバル変数として利用しても Rule に引っかかりません。
各ファイル毎の出力されているメッセージに対応する
src/main/assets/js/inquiry/input01.js
'idList' is assigned a value but never used
は宣言した変数を関数内で使用していないことが原因なので、変数を宣言している行を削除します。'validator' is already declared in the upper scope
はソースの上で宣言しているvar validator = require("lib/util/validator.js");
と同じvalidator
という変数を使用していたので、validator
→validateFunction
に変更します。
src/main/assets/js/inquiry/input02.js
'validator' is already declared in the upper scope
は input01.js と同様にvalidator
→validateFunction
に変更します。A space is required after '{'
とA space is required before '}'
は IntelliJ IDEA のフォーマッターがスペースを入れないようになっているので、この Rule を無効に変更します。.eslintrc.js を以下のように変更します。
"rules": { .......... // require padding inside curly braces 'object-curly-spacing': ['error', 'never'] }
'event' is already declared in the upper scope
はevent
→e
に変更します。
src/main/assets/js/lib/class/Form.js
'Form' was used before it was defined
はmodule.exports = Form;
の後にfunction Form(idList) { ... }
の記述があるために出力されていますが、module.exports
の記述を先頭にしたいので .eslintrc.js にno-use-before-define
の設定を node_modules/eslint-config-airbnb-base/rules/variables.js からコピーしてfunctions: true
→functions: false
に変更します。
"rules": { .......... // disallow use of variables before they are defined 'no-use-before-define': ['error', { functions: false, classes: true, variables: true }] }
Line 21 exceeds the maximum line length of 100
は1行の最大値を 120 に変更したいので、.eslintrc.js にmax-len
の設定を node_modules/eslint-config-airbnb-base/rules/style.js からコピーして100
→120
に変更します。
"rules": { .......... // specify the maximum length of a line in your program // http://eslint.org/docs/rules/max-len 'max-len': ['error', 120, 2, { ignoreUrls: true, ignoreComments: false, ignoreRegExpLiterals: true, ignoreStrings: true, ignoreTemplateLiterals: true }] }
Assignment to property of function parameter 'form'
は関数の引数で渡された変数を変更しようとしているから出ているメッセージらしいのですが、form を引数に渡さないようにする方法が今だによく分かっていないので、ソースの先頭に/* eslint-disable no-param-reassign,lines-around-directive */
を記述して Form.js だけこの Rule を無効にします。
src/main/assets/js/lib/util/converter.js
Multiple spaces found before ...
はソースの右側にスペース複数を入れてからコメントを書いていたためでした。コメントは移動してコメントだけの行になるようにします。
以上で全ての対応が完了です。
最後に
動きはしていたので今回はリンターを導入してもそんなに指摘は受けないかな、と思っていましたが、いつものように結構エラーが出ました。。。 ESLint は eslint-config-airbnb や eslint-config-airbnb-base といった使える設定が公開されているし、導入例の記事もいろいろ公開されていたので導入しやすかったです。また Airbnb JavaScript Style Guide は有名なようなので、普段 Javascript を実装しない自分としては読んでおきたいと思います。
また今回の調査をしていた時に prettier という Javascript のフォーマッターの記事を見かけました。Using External tools: ESLint autofix, React Native and Prettier を見ると IntelliJ IDEA でもサポートされているようなので、時間があれば見てみたいと思います。
最後に .eslintrc.js の最終版を載せておきます。
module.exports = { "extends": "airbnb-base/legacy", "env": { "browser": true, "jquery": true }, "rules": { // requires to declare all vars on top of their containing scope 'vars-on-top': 'off', // require function expressions to have a name // http://eslint.org/docs/rules/func-names 'func-names': 'off', // enforce consistent line breaks inside function parentheses // https://eslint.org/docs/rules/function-paren-newline 'function-paren-newline': 'off', // this option sets a specific tab width for your code // http://eslint.org/docs/rules/indent indent: ['error', 4, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1, // MemberExpression: null, FunctionDeclaration: { parameters: 1, body: 1 }, FunctionExpression: { parameters: 1, body: 1 }, CallExpression: { arguments: 1 }, ArrayExpression: 1, ObjectExpression: 1, ImportDeclaration: 1, flatTernaryExpressions: false, ignoredNodes: ['JSXElement', 'JSXElement *'] }], // disallow mixed 'LF' and 'CRLF' as linebreaks // http://eslint.org/docs/rules/linebreak-style 'linebreak-style': ['error', 'windows'], // specify whether double or single quotes should be used quotes: ['error', 'double', { avoidEscape: true }], // require padding inside curly braces 'object-curly-spacing': ['error', 'never'], // disallow use of variables before they are defined 'no-use-before-define': ['error', { functions: false, classes: true, variables: true }], // specify the maximum length of a line in your program // http://eslint.org/docs/rules/max-len 'max-len': ['error', 120, 2, { ignoreUrls: true, ignoreComments: false, ignoreRegExpLiterals: true, ignoreStrings: true, ignoreTemplateLiterals: true }] } };
履歴
2017/11/05
初版発行。
2017/11/06
* "browser": true,
は node_modules/eslint-config-airbnb-base/legacy.js で設定済なので削除しました。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( 番外編 )( webpack で jQuery だけバンドルしないで外部ファイルを利用するには? )
概要
記事一覧はこちらです。
既存のページで既に jQuery を使用しており、そこに webpack でバンドルした js ファイルを導入したい場合に、jQuery だけバンドルしないで外部ファイルの jQuery を利用する方法が知りたくなったので調べてみます。
参照したサイト・書籍
webpackでCDNから取ってきたりした外部のjQueryなどを利用する方法
http://frontend-takuyan.hateblo.jp/entry/jquery-in-webpackwebpack - Externals
https://webpack.js.org/configuration/externals/
目次
本文
webpack.config.js を変更する
webpackでCDNから取ってきたりした外部のjQueryなどを利用する方法 の記事にずばりその通りの内容が記載されていました。webpack.config.js に以下の記述を追加すればいいそうです。
var webpack = require('webpack'); module.exports = { entry: { "js/app": ["./src/main/assets/js/app.js"], "js/inquiry/input01": ["./src/main/assets/js/inquiry/input01.js"], "js/inquiry/input02": ["./src/main/assets/js/inquiry/input02.js"], "js/inquiry/input03": ["./src/main/assets/js/inquiry/input03.js"], "js/inquiry/confirm": ["./src/main/assets/js/inquiry/confirm.js"] }, output: { path: __dirname + "/src/main/resources/static", publicPath: "/", filename: "[name].js" }, resolve: { modules: [ "node_modules", "src/main/assets/js" ], alias: { jquery: "jquery" } }, externals: { jquery: "jQuery" }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ] };
externals: { jquery: "jQuery" }
を追加します。ただし"jquery"
ではなく、必ず"jQuery"
と記述する必要があります。"jquery"
だと正常に動作しませんでした。
動作確認
src/main/resources/static/vendor の下に node_modules/jquery/dist/jquery.min.js をコピーします。
src/main/resources/templates/web/inquiry/input01.html, input02.html を以下のように変更します。
<!-- REQUIRED JS SCRIPTS --> <script src="/vendor/jquery.min.js"></script> <script src="/js/inquiry/input01.js"></script> </body> </html>
- 各画面用の js ファイルの上に
<script src="/vendor/jquery.min.js"></script>
を追加します。
コマンドプロンプトを起動して npm run springboot
コマンドを実行し、Tomcat も起動します。
http://localhost:9080/inquiry/input/01/ にアクセスすると入力画面1が表示されます。
「次へ」ボタンをクリックすると入力チェックのエラーメッセージが表示されます。Javascript の処理が問題なく動作していますね。
autokana も動作しており、またデータを全て入力すると入力チェックOKの状態になります。
「次へ」ボタンをクリックして入力画面2へ遷移した後、郵便番号を入力すると、ajax でデータを取得して autocomplete で住所の選択肢も表示されます。
動作も問題なさそうです。
履歴
2017/11/03
初版発行。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その32 )( npm の admin-lte package から jQuery がなくなっていたので対応する )
概要
記事一覧はこちらです。
- 今回の手順で確認できるのは以下の内容です。
参照したサイト・書籍
目次
手順
git clone から動作させようとしてエラーが出るところまでやってみる
git clone する
C:\project-test フォルダを作成して、この下に git clone します。
コマンドプロンプトを起動して C:\project-test フォルダに移動した後、git clone -b feature/1-issue https://github.com/ksby/ksbysample-boot-miscellaneous.git
コマンドを実行します。
C:\project-test の下に ksbysample-boot-miscellaneous フォルダが作成され、その下に boot-npm-geb-sample プロジェクトが出来ます。
IntelliJ IDEA で boot-npm-geb-sample プロジェクトを開く
IntelliJ IDEA の「Welcome to IntelliJ IDEA」ダイアログを表示した後、「Open」ボタンをクリックします。
「Open File or Project」ダイアログが表示されますので、C:\project-test\ksbysample-boot-miscellaneous\boot-npm-geb-sample フォルダを選択して「OK」ボタンをクリックします。
「Import Project from Gradle」ダイアログが表示されますので、「Create directories for empty content roots automatically」をチェックした後「OK」ボタンをクリックします。
IntelliJ IDEA のメイン画面が開きますので、画面右下の処理中を示すプログレスバーが消えるまで待ちます。
処理が終了すると画面右側の Gradle projects が下の画像のようになります。
npm install コマンドを実行する
コマンドプロンプトで C:\project-test\ksbysample-boot-miscellaneous\boot-npm-geb-sample へ移動した後、npm install
コマンドを実行します。
次に npm run springboot
コマンドを実行します。。。が、Module not found: Error: Can't resolve 'admin-lte/plugins/jQuery/jquery-2.2.3.min.js' in ...
というエラーメッセージが出力されました。なぜか jquery のファイルがないようです。。。
clean タスク → Rebuild Project → build タスクを実行する
IntelliJ IDEA から clean タスク → Rebuild Project → build タスクを実行してみると、こちらは BUILD SUCCESSFUL のメッセージが出力されて正常に終了しました。
エラーの原因を修正する
以前は node_modules/admin-lte/plugins/jQuery/jquery-2.2.3.min.js というファイルが存在したのですが、node_modules/admin-lte/plugins の下を見ると jQuery フォルダ自体がなくなっていました。。。 admin-lte は jQuery を同梱しなくなったんですね。
ということであれば普通に npm で jQuery をインストールして、そちらを使うように変更します。
まずは npm install --save jquery
コマンドを実行して jQuery をインストールします。
次に webpack.config.js を以下のように変更します。
var webpack = require('webpack'); module.exports = { entry: { "js/app": ["./src/main/assets/js/app.js"], "js/inquiry/input01": ["./src/main/assets/js/inquiry/input01.js"], "js/inquiry/input02": ["./src/main/assets/js/inquiry/input02.js"], "js/inquiry/input03": ["./src/main/assets/js/inquiry/input03.js"], "js/inquiry/confirm": ["./src/main/assets/js/inquiry/confirm.js"] }, output: { path: __dirname + "/src/main/resources/static", publicPath: "/", filename: "[name].js" }, resolve: { modules: [ "node_modules", "src/main/assets/js" ], alias: { jquery: "jquery" } }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ] };
"admin-lte/plugins/jQuery/jquery-2.2.3.min.js"
→"jquery"
に変更します(全部で3ヶ所)。
これで変更は完了です。再び npm run springboot
コマンドを実行してみると、今度はエラーメッセージは出ませんでした(下の方に白い文字で出ているメッセージは Tomcat が起動していないことによるものです)。
Tomcat を起動して。。。と思ったら spring.profiles.active を指定していないことによるエラーが出ました。
IntelliJ IDEA のメインメニューから「Run」-「Edit Configurations...」を選択し、「Run/Debug Configurations」ダイアログが表示されたら、画面左側の一覧から「Spring Boot」-「Application」を選択して、画面右側の「VM options」に -Dspring.profiles.active=develop -Dfile.encoding=UTF-8
を入力後「OK」ボタンをクリックします。
再度 Tomcat を起動したら、今度は問題なく起動しました。
ブラウザで http://localhost:9080/inquiry/input/01/ にアクセスすると入力画面1も表示されます。
何も入力せずに「次へ」ボタンを押すと入力エラーのある入力項目にエラーメッセージが表示されますので、Javascript も正常に動作しているようです。
ちょっとつまずきましたが、以前と比較すると試せるようになるまで全然簡単になりました。Edit Configuration の設定も何か簡単に出来る方法があるといいのですが。。。と思い Web で検索すると以下の記事を見つけました。これまで使用したことがありませんでしたが、share チェックボックスをチェックすればよいようです。そのうち試してみたいと思います。
How do I share IntelliJ Run/Debug configurations between projects? https://stackoverflow.com/questions/24642147/how-do-i-share-intellij-run-debug-configurations-between-projects
履歴
2017/11/01
初版発行。
Java SE を 8u144 → 8u152 へバージョンアップ
※ksbysample-webapp-lending プロジェクトを開いた状態でバージョンアップしています。
Java SE を 8u144 → 8u152 へバージョンアップする
Oracle の Java SE Downloads を見ると 8u152 がダウンロードできるようになっていました。以下のページに説明があります。
- [Java] Updates for Java SE Platform
https://orablogs-jp.blogspot.jp/2017/10/updates-for-java-se-platform.html
8u152 へバージョンアップします。
- [Java] Updates for Java SE Platform
jdk-8u152-windows-x64.exe をダウンロードして C:\Java\jdk1.8.0_152 へインストールした後、環境変数 JAVA_HOME のパスを C:\Java\jdk1.8.0_152 へ変更します。
コマンドプロンプトから
java -version
を実行し、1.8.0_152
に変更されていることを確認します。開いているプロジェクトを閉じて「Welcome to IntelliJ IDEA」ダイアログを表示します。
ダイアログ下部の「Configure」-「Project Defaults」-「Project Structure」を選択します。
「Default Project Structure」ダイアログが表示されます。画面左側で「Project Settings」-「Project」を選択後、画面右側の「Project SDK」の「New...」ボタンをクリックし、表示されるメニューから「JDK」を選択します。
「Select Home Directory for JDK」ダイアログが表示されます。C:\Java\jdk1.8.0_152 を選択した後、「OK」ボタンをクリックします。
「Default Project Structure」ダイアログに戻るので、今度は「Project SDK」の「Edit」ボタンをクリックします。
画面左側で「Platform Settings」-「SDKs」が選択された状態になるので、画面右上の入力フィールドで "1.8" → "1.8.0_152" へ変更します。
次に中央のリストから「1.8.0_144」を選択した後、リストの上の「-」ボタンをクリックして削除します。
「OK」ボタンをクリックして「Default Project Structure」ダイアログを閉じます。
「Welcome to IntelliJ IDEA」ダイアログに戻ったら、ksbysample-webapp-lending プロジェクトを開きます。
IntelliJ IDEA のメイン画面が開いたら、メニューから「File」-「Project Structure...」を選択します。
「Project Structure」ダイアログが表示されます。以下の画像の状態になっているので、
「Project SDK」と「Project language level」を選択し直します。
「OK」ボタンをクリックして「Project Structure」ダイアログを閉じます。
メイン画面に戻ると画面右下に「Indexing...」の表示が出るので、終了するまで待ちます。
clean タスク実行 → Rebuild Project 実行 → build タスクを実行して、"BUILD SUCCESSFUL" のメッセージが出力されることを確認します。
Project Tool Window で src/test を選択した後、コンテキストメニューを表示して「Run ‘All Tests’ with Coverage」を選択し、テストが全て成功することを確認します。
特に問題は発生しませんでした。8u152 で開発を進めます。