Ember.js 3系のチュートリアルをやってみた

ワタシは社内で、Ember.js を使った開発に携わっているのですが、検索しても、とにかく日本語の情報が少ないです。
ブームであったであろう3系より前の情報でしたら、チラホラ程度には見つかるのですが。。
[英語の公式サイトのチュートリアル](https://guides.emberjs.com/release/tutorial/ember-cli/)を進めるために試行錯誤しましたので、これから始められる方の一助になればと思い、まとめてみました。

## この記事でまとめたチュートリアルの内容について
初めに、[クイックスタート](https://guides.emberjs.com/release/getting-started/quick-start/)から開始され、これはすぐに終わるのですが、これに続く、[チュートリアル](https://guides.emberjs.com/release/tutorial/ember-cli/)はそれなりにボリュームがあります。
チュートリアル中で記述するコードは、バージョン依存がありますので、インストールされたバージョンのものを参照するようにします。
コード以外の解説部分は、バージョンが異なってもほぼ問題ないかと思い、[日本語訳のもの](https://emberjs-japan.github.io/guides-ja-preview/tutorial/ember-cli/)を参照しました。
チュートリアルの[Application Tests](https://guides.emberjs.com/release/tutorial/subroutes/)の6項目にパスするまでを対象としています。
[チュートリアル](https://guides.emberjs.com/release/tutorial/ember-cli/)の流れに沿って進めていくので、詰まりそうになる箇所のみのまとめとしています。

## 検証環境について
参考までに、チュートリアルを検証したワタシの環境を載せておきます。

* [Windows Subsystem for Linux（WSL）Ubuntu 18.04](https://www.microsoft.com/ja-jp/p/ubuntu-1804/9n9tngvndl3q)
* node.js : 8.11.4
* ember-cli : 3.3.0

## [Creating a New App](https://guides.emberjs.com/release/tutorial/ember-cli/#toc_creating-a-new-app)
[クイックスタート](https://guides.emberjs.com/release/getting-started/quick-start/)を実施された方は、同じところで詰まったかもしれません。
Ember.js のバージョンが 3.1.X の場合、`ember new super-rentals`で `npm⠋` の処理で時間がかかり
`Error creating new application`が返ってきて、チュートリアル通りに進まない事例がありました。
開発サーバ起動までを下記の要領で進めました。

```
1 | ember new super-rentals --skip-npm
2 | cd super-rentals
3 | npm install
4 | ember s
```
## [Testing Our Application As We Go](https://guides.emberjs.com/release/tutorial/acceptance-test/#toc_testing-our-application-as-we-go)
`tests/acceptance/list-rentals-test.js`を編集する際に、以降チュートリアルに準じて進めるため、下記の箇所の上下を入れ替えておきました。
それ以外の箇所は、チュートリアル通りに編集します。`-  `で始まる行は削除、`+  `で始まる行は追記のようです。

```
2 | import { visit, currentURL } from '@ember/test-helpers';
3 | import { setupApplicationTest } from 'ember-qunit';
```

## [Ember's test helpers](https://guides.emberjs.com/release/tutorial/routes-and-templates/#toc_embers-test-helpers)
チュートリアルでは、編集対象のファイルが`/tests/acceptance/list-rentals-test.js`と絶対パスになっていますが、
`tests/acceptance/list-rentals-test.js`と相対パスに読み替えます。
先で、上下を入れ替えた`import { visit, currentURL } from '@ember/test-helpers';`をチュートリアルに準じ、
複数行になるように書き換えます。

## [Application Testing the Rental List](https://guides.emberjs.com/release/tutorial/model-hook/#toc_application-testing-the-rental-list)
`tests/acceptance/list-rentals-test.js`で`4 | +  visit`と追記するよう記述がありますが、記述済みのため、追記不要です。
それ以外は、チュートリアル通りに追記します。
編集対象のファイルは、一部のみ掲載されていますので、`+  `部分の上下の記述を参考にして追記します。

## [ember-cli-tutorial-style](https://guides.emberjs.com/release/tutorial/installing-addons/#toc_ember-cli-tutorial-style)
開発サーバを起動中の場合、アドオンインストール後は、チュートリアルにあるように、`Ctrl + c` で一旦停止後、
再度、`ember s` を実行して、開発サーバを起動し直します。そうしないと、アドオンが適用されないようです。

## [ember-cli-mirage](https://guides.emberjs.com/release/tutorial/installing-addons/#toc_ember-cli-mirage)
Ember.js のバージョンが 3.1.X の場合、アドオンインストール時に下記メッセージが表示されることがありました。

```
******************************************************
destroy-app.js helper is not present. Please read this
https://gist.github.com/blimmer/35d3efbb64563029505a
to see how to fix the problem.
******************************************************
```
その場合、`tests/helpers/destroy-app.js`に[参照先を参考にして](https://github.com/samselikoff/ember-cli-mirage/pull/281#discussion-diff-39935222)下記のものを設置いたしました。

```
import {run} from '@ember/runloop'

export default function destroyApp (application) {
  run(application, 'destroy')
  server.shutdown()
}
```
## [An Integration Test](https://guides.emberjs.com/release/tutorial/simple-component/#toc_an-integration-test)
`tests/integration/components/rental-listing-test.js`の編集箇所が多いですが、最終形態が一番下にありますので、
最終形態のものを拝借して、全置換えすると、編集ミスが少なくて済みそうです。

## [Setting up Application Tests to use Mirage](https://guides.emberjs.com/release/tutorial/ember-data/#toc_setting-up-application-tests-to-use-mirage)
Testemテストランナーが実行されたままの状態の場合、テスト実行結果で、下記のメッセージが表示される事例がありました。

```
Error: Could not find module `ember-cli-mirage/test-support/setup-mirage` imported from `super-rentals/tests/acceptance/list-rentals-test`
```
cliで実行中のTestemテストランナーを`q`を押して、一旦停止して、再度、`ember t -s`で実行しなおすと、問題が解消されました。

## [Providing Markup to a Component](https://guides.emberjs.com/release/tutorial/autocomplete-component/#toc_providing-markup-to-a-component)
`app/templates/rentals.hbs`を編集の際に、末尾に`{{outlet}}`を追記しておきました。
以降のチュートリアルで同じファイルを編集する場合、突然`{{outlet}}`が出現します。

## [Faking Query Results](https://guides.emberjs.com/release/tutorial/autocomplete-component/#toc_faking-query-results)
`mirage/config.js`は以前に編集していますが、例えば`description:`などの行が突然と現れます。
全内容を拝借して、`+  `から始まる行は先頭の`+  `のみを削除、`-  `から始まる行は、行ごと削除しました。

## [An Integration Test](https://guides.emberjs.com/release/tutorial/autocomplete-component/#toc_an-integration-test)
`tests/integration/components/list-filter-test.js`の編集箇所が多く、最終形態が解りにくいです。
以下のようになりました。

```
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled, triggerKeyEvent, fillIn } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { resolve } from 'rsvp';

const ITEMS = [{city: 'San Francisco'}, {city: 'Portland'}, {city: 'Seattle'}];
const FILTERED_ITEMS = [{city: 'San Francisco'}];

module('Integration | Component | list-filter', function(hooks) {
  setupRenderingTest(hooks);

  test('should initially load all listings', async function (assert) {
    // we want our actions to return promises,
    //since they are potentially fetching data asynchronously
    this.set('filterByCity', () => resolve({ results: ITEMS }));

    // with an integration test,
    // you can set up and use your component in the same way your application
    // will use it.
    await render(hbs`
      {{#list-filter filter=(action filterByCity) as |results|}}
        <ul>
        {{#each results as |item|}}
          <li class="city">
            {{item.city}}
          </li>
        {{/each}}
        </ul>
      {{/list-filter}}
    `);

    return settled().then(() => {
      assert.equal(this.element.querySelectorAll('.city').length, 3);
      assert.equal(this.element.querySelector('.city').textContent.trim(), 'San Francisco');
    });

  });

  test('should update with matching listings', async function (assert) {
    this.set('filterByCity', (val) =>  {
      if (val === '') {
        return resolve({
          query: val,
          results: ITEMS });
      } else {
        return resolve({
          query: val,
          results: FILTERED_ITEMS });
      }
    });

    await render(hbs`
      {{#list-filter filter=(action filterByCity) as |results|}}
        <ul>
        {{#each results as |item|}}
          <li class="city">
            {{item.city}}
          </li>
        {{/each}}
        </ul>
      {{/list-filter}}
    `);

    // fill in the input field with 's'
    await fillIn(this.element.querySelector('.list-filter input'),'s');
    // keyup event to invoke an action that will cause the list to be filtered
    await triggerKeyEvent(this.element.querySelector('.list-filter input'), "keyup", 83);

    return settled().then(() => {
      assert.equal(this.element.querySelectorAll('.city').length, 1, 'One result returned');
      assert.equal(this.element.querySelector('.city').textContent.trim(), 'San Francisco');
    });
  });
});
```
## [Application Tests](https://guides.emberjs.com/release/tutorial/autocomplete-component/#toc_application-tests)
もし、ブラウザでのテスト結果が、チュートリアルと異なっている場合、cliで実行中のTestemテストランナーを
`q`を押して、一旦停止して、再度、`ember t -s`で実行しなおすことで同じになりました。

## [Fetching Maps With a Service](https://guides.emberjs.com/release/tutorial/service/#toc_fetching-maps-with-a-service)
`app/services/maps.js`を編集する場合、`19 |+   getMapElement(location) {`の行先頭の`+   `が抜けていますので、
一緒に追記しました。

## [Display Maps With a Component](https://guides.emberjs.com/release/tutorial/service/#toc_display-maps-with-a-component)
これまでの編集内容に問題がない状態で、マップが表示されない場合、`Ctrl + c` で開発サーバを一旦停止後、再度、
`ember s` を実行するとマップが表示されるようになりました。

## おわりに
一通り、詰まりそうな箇所は、チュートリアルのあたまから順番に取り上げてみましたが、いかがでしたでしょうか？
他に詰まりそうな箇所が出てきましたら、随時追加していきます。

## 参考サイト
[Ember.js 公式サイト](https://guides.emberjs.com/release/)
[公式サイトの日本語訳](https://emberjs-japan.github.io/guides-ja-preview/)