Mainly Devel Notes

Twitter, GitHub, StackOverflow: @ovrmrw (short hand of "overmorrow" that means the day after tomorrow)

Angular2でDIしてテストを書いたけどhtmlファイルの重複をなんとかしたかった。

index.htmlとindex.test.htmlの重複排除, テスト(Jasmine), System.js, Angular2, Dependency Injection

前回↓の記事の続きです。
Angular2でDI(依存性注入)してテスト(Jasmine)を書いてみた。

前回は本番用のindex.htmlとテスト用のindex.test.htmlはほぼ同じ内容で書いて用意しておく、 みたいな感じでしたが、それだとindex.htmlに何か変更を加えたらindex.test.htmlも直さないといけなくなるので、 そういうかったるい重複もなんとかならないかなと思って色々やってみたらできた、というのが今回の趣旨です。
(できたと思ったらChromeはOKでFirefoxではNGでした。他のブラウザでは試していません。)
↑修正しました。Windows10のChrome, Firefox, Edgeで動作確認済みです。

こういうの需要があるのかわかりませんがまとめておかないと僕が忘れてしまうので。

まずは準備

npm init -y
npm install angular2 systemjs jquery system-text --save
npm install live-server typescript jasmine-core --save-dev

前回の記事から増えているのはsystem-textです。これはSystem.jsでテキストファイルを importできるようにするためのプラグインです。

(root)/src/system.config.js

System.config({
  baseURL: '/',
  paths: {
    '*': 'src/*',
    'node:*': 'node_modules/*'
  },
  map: {
    'text': 'node:system-text/text.js',
    'jquery': 'node:jquery/dist/jquery.js'
  },
  packages: {
    'app': { defaultExtension: 'js' }
  },
  meta: {
    '*.html': { loader: 'text' }
  }
});

今回のケースではSystem.config()は上記のようになります。
1.maptextの記述を加えています。
2.metaプロパティを追加して、.htmlファイルをimportするときはsystem-textプラグインを使うよう指定しています。

(root)/src/index.html (npm startのときに呼ばれる)

<html>
  <head>
    <title>Angular 2</title>
    <script src="../node_modules/systemjs/dist/system.src.js"></script>             
    <script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
    <script src="system.config.js"></script>      
  </head>
  <body>    
    <script>
      System.import('app/app');
    </script>
    <my-app>loading...</my-app>
  </body>
</html>

npm startしたときに呼ばれるファイルです。Jasmineの気配は微塵もありません。テストする気は無いのかとさえ思えます。

(root)/src/index.test.html (npm testのときに呼ばれる)【今回のエントリーの要です】

<title>Angular 2 Test</title>
<link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>

<script src="../node_modules/systemjs/dist/system.src.js"></script>
<script src="system.config.js"></script>
<script>
  System.import('jquery')
    .then(function () {
      return System.import('./index.html')
    })
    .then(function (html) {
      $('inject-here').append(html);      
    })
    .then(function () {
      return System.import('app/app.spec').then(window.onload);
    });
</script>

<inject-here></inject-here>

npm testしたときに呼ばれるファイルです。<html>タグもありません。
このファイルがロードされるとまずはJasmineのCSSJavaScriptライブラリを読み込みます。
その後System.import()を使うためにSystem.jsのライブラリとsystem.config.jsを読み込みます。
System.import()の部分では、
1.jQueryのライブラリを読み込んで、
2.index.htmlを文字列として読み込んで、
3.<inject-here>タグに流し込んで、
4.app.jsのテストファイルであるapp.spec.jsを読み込む、
という処理を行なっています。
(app.spec.jsはJasmineを使っているので.then(window.onload)は決まり文句です)

これにより本番用のindex.htmlからテスト用の記述を排除し、index.htmlをどのように変更しても index.test.htmlは影響を受けない状態となりました。これはJasmineのテストを書くときに汎用性のある手法かもしれません。

(当初これがChrome以外では動作しなくてかなり悩んだのですが、Promiseチェーンの最後のapp.spec.jsのimportもreturnする のがコツみたいです。詳細はよくわかりません)

package.jsonの"scripts"

"scripts": {
  "test": "live-server --open=src/index.test.html",
  "start": "live-server --open=src",
  "tsc": "./node_modules/.bin/tsc -p . -w"
},

本番はnpm start、テストはnpm testです。(npm run tscでTypeScriptファイルをコンパイルします)


以上です。ありがとうございました。