jestを使ってVue3のUnitテストをやってみようとして苦労した話

# 背景

[前回の記事](WebAssemblyモジュールを含むwebアプリをAWSを使って公開してみる)にて、web上に自分のアプリを公開してみました。
そんな中、ちょっと不具合とかも見つかり修正したいと思いました。操作ステップの順番が多岐に渡るため、何か修正した時に、今まで動いていた操作順番での動作が崩れるのは避けたい所です。
既存の仕様をちゃんと定義しておいて、今後機能を追加する時でもそれを崩さない様にしながら行いたい所です。そんな頻繁にこのアプリに手を加える訳でもないので、時間が空いてしまった時などに仕様を思い出すのに時間がかかるというのも避けたい所です。

TDDをやるべきという事になります。

# 今回の記事の前提技術

- yarn
- Vue3
- Vuetify（2022年5月14日現在、β版です）


# テスト実行準備

まず、Unitテストをを実行出来る様にするまで準備します。

## jestインストール

Vueの公式ページではjestを使うのが一番シンプルだと言っています。

https://test-utils.vuejs.org/installation/

vueのプロジェクト作成（vue create <project name>）時、以下の様な選択肢が出てきたかと思いますが、作成当時Defaultを選択していると、test用フレームワークは入らない様です。プロジェクト作成時に指定するのが一番無難なはずなので、この記事を見た方でこれからVueプロジェクトを作成する場合にはシンプルにそこでtest用フレームワークを選択した方が良いと思います。

```console
Vue CLI v5.0.4
? Please pick a preset: (Use arrow keys)
❯ Default ([Vue 3] babel, eslint) 
  Default ([Vue 2] babel, eslint) 
  Manually select features 
```

公式ページ通りにやってい行きます。

```console
yarn add --dev @vue/test-utils@next
```

vue-jestのVer5を使うのがよいという事ですが、Vueの公式ページのリンクから飛べるvue-jestのgitリポジトリによると最新のjestではパッケージ名が`@vue/vue3-jest`となる様です。

```console
yarn add @vue/vue3-jest --dev
```

これだけだと、テスト実行用コマンドが実行出来ません。ダミープロジェクト作成時に`Manually select features`を選択すると、機能が選べるので`Unit Testing`を選択、`Pick a unit testing solution:`で`Jest`を選択します。そうして出来たプロジェクトの`package.json`をみるといくつか必要なモジュールがありそうだったので、それをインストールします。

```console
yarn add @types/jest @vue/cli-plugin-unit-jest babel-jest jest ts-jest --dev
```

ここまでで`yarn jest`が実行出来る様になりました。テストケースが無くてエラーになってるという状態ですね。

```console:yarn jest実行結果
yarn run v1.22.18
$ vue-cli-service test:unit
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In /vue/cubetrain
  17 files checked.
  testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
  testPathIgnorePatterns: /node_modules/ - 17 matches
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
```

メッセージからすると正規表現`**/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x)`に合致するファイルがテストの対象となる事が解ります。

## テストケース追加

公式ページにある下記内容で`src/components/__tests__/SampleTest.ts`ファイルを追加します。
その後、`yarn jest`を実行します。

```typescript:SampleTest.ts
import { mount } from '@vue/test-utils'

// The component to test
const MessageComponent = {
  template: '<p>{{ msg }}</p>',
  props: ['msg']
}

test('displays message', () => {
  const wrapper = mount(MessageComponent, {
    props: {
      msg: 'Hello world'
    }
  })

  // Assert the rendered text of the component
  expect(wrapper.text()).toContain('Hello world')
})
```

## エラー解消

エラーが出てしまったので解消していきます。

```console
  ● displays message

    The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string.
    Consider using the "jsdom" test environment.
    
    ReferenceError: document is not defined

       8 |
       9 | test('displays message', () => {
    > 10 |   const wrapper = mount(MessageComponent, {
         |                   ^
      11 |     props: {
      12 |       msg: 'Hello world'
      13 |     }

      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:7840:14)
      at Object.<anonymous> (src/components/__tests__/WasmScreenTest.ts:10:19)
```

### package.jsonの修正

先ほどと同様に、最初からjestを導入したダミープロジェクトを調べると、以下の差分が`package.json`にあったので追加してみます。

```jsonc:package.json
{
  //中略
  "jest": {
    "preset": "@vue/cli-plugin-unit-jest/presets/typescript-and-babel"
  }
  //中略
}
```

[jestの公式ページ](https://archive.jestjs.io/docs/ja/configuration#preset-string)によるとjest用に事前準備された設定群の様です。名前からしてもVueでjestを使う為の設定セットという事になると思います。これを追記しておく必要があるという事ですね。

再度`yarn jest`を実行すると違うエラーが出るようになりました。確かにVSCode上でもtestとexpectには赤下線が出ていました。

```console
  ● Test suite failed to run

    src/components/__tests__/WasmScreenTest.ts:9:1 - error TS2593: Cannot find name 'test'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.

    9 test('displays message', () => {
      ~~~~
    src/components/__tests__/WasmScreenTest.ts:17:3 - error TS2304: Cannot find name 'expect'.

    17   expect(wrapper.text()).toContain('Hello world')
         ~~~~~~
```

### tsconfig.jsonの修正

もう少し先ほどのダミープロジェクトとの差分を探ってみるとtsconfig.jsonにjestに関係ある差分があったので追加してみます。

```jsonc
{
  "compilerOptions": {
    "target": "esnext",
    // 中略
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "jest" // この行を追加
    ],
```

[TypeScriptの公式ページ](https://www.typescriptlang.org/tsconfig#types)の説明によるとTypeScriptで`node_modules/@types`配下のファイルを読み込んでおくための設定の様です。これも追記しておく必要があるという事ですね。

再度`yarn jest`を実行した所成功しました。

```console
# yarn jest
yarn run v1.22.18
$ vue-cli-service test:unit
 PASS  src/components/__tests__/SampleTest.ts
  ✓ displays message (20 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.752 s, estimated 5 s
Ran all test suites.
Done in 4.21s.

```

# テスト技術要素確認

テスト実行が成功する様になったので、作り方を確認していきます。

## サンプル確認

jestを初期導入しているダミープロジェクトでは、テストケースもついています。その中身を見ていきます。先ほど使ったテストと書き方が違う部分があります。

```typescript:example.spec.ts
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallowMount(HelloWorld, {
      props: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})
```

### describe

テストケースを含有している形で記述されています。
[jestの公式ページ](https://jestjs.io/ja/docs/api#describename-fn)によると、必須ではないが、グルーピングするのに便利との事です。ネストする事も出来たり、describe.eachなど派生関数？もあり同じテストを繰り返す時とかに便利そうです。最低でもコンポーネント毎に一つのdescribeでまとめておいた方が良さそうです。

### it

同じく[jest公式ページ](https://jestjs.io/ja/docs/api#testname-fn-timeout)によると、`test`の別名との事です。testは別名という形の記述なので、`it`の方が主流なのかな、と思います。

## 基本命令確認

UIのテストをする上で、どんな事が出来るのか確認します。ポイントとしては以下の点になるかと思います。

- UIのどんなイベントを発生させることが出来るのか
- その結果としてどんな情報を取得する事が出来るのか
- 予測と結果の比較ではどんな関数が用意されているのか

[Vue公式ページ](https://test-utils.vuejs.org/guide/essentials/a-crash-course.html#adding-a-new-todo)の説明から読み取っていきます。

```typescript
import { mount } from '@vue/test-utils'
import TodoApp from './TodoApp.vue'

test('creates a todo', () => {
  const wrapper = mount(TodoApp)
  expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(1)

  wrapper.get('[data-test="new-todo"]').setValue('New todo')
  wrapper.get('[data-test="form"]').trigger('submit')

  expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(2)
})
```

### イベント発火前

#### mount

mountを使う事で、コンポーネントのレンダリングをする事が出来る様です。

#### 要素取得（findAll、getなど）

[Vue公式ページ - Wrapper methods](https://test-utils.vuejs.org/api/#wrapper-methods)を見ると、findAll、getなどの関数でhtml要素の取得が出来る様です。

#### 要素への情報設定（setValue）

[Vue公式ページ - Wrapper methods - setValue](https://test-utils.vuejs.org/api/#setvalue)を見ると、先ほどの要素取得関数でDOM要素を取得した後、そのDOM要素に値を設定する事が出来る様です。

#### 初期Vue属性設定（data、props）

VueではDOM要素に直接値を設定するのでなく、propsやdataで情報をセットする事が多いと思います。
同、[data](https://test-utils.vuejs.org/api/#data)、[props](https://test-utils.vuejs.org/api/#props)を使うとそれらの値設定が可能な様です。

#### Vue属性設定（setData、setProps）

前述の初期設定だけでなく、レンダリングした後にもそれらの値を変更するケースがあると思います。
同、[setData](https://test-utils.vuejs.org/api/#setdata)、[setProps](https://test-utils.vuejs.org/api/#setprops)を使うとそれらの値設定が可能な様です。

#### イベント発火（trigger）

同、[trigger](https://test-utils.vuejs.org/api/#trigger)メソッドで各種イベントを発生させるようです。wrapperとして各種命令が用意されているのではなく、trigger関数の引数で発生させるイベントを指定する形ですね。

### 結果取得

一通り見ると以下の手法が用意されている様です。

#### DOM要素属性（attrs）

DOM要素の選択は値セットの時に出てきましたが、その属性値を取得してテストする事が出来る様です。
同[attrs](https://test-utils.vuejs.org/api/#attrs)で行う事になると思います。

#### 可視性（isVisible）

同[isVisible](https://test-utils.vuejs.org/api/#isvisible)で、指定要素が見えるかのチェックが行える様です。

#### 文字列（html、text）

同[html](https://test-utils.vuejs.org/api/#html)、[text](https://test-utils.vuejs.org/api/#text)で、指定要素のhtml文字列やtextのチェックが行えるようです。

#### Vue属性

ボタンを押してdataなどが変わるケースがあると思いますが、それを直接チェックするメソッドは見た所なさそうです。そのdataによりレンダリングが変更されたDOM要素の各種要素をチェックする方向になりそうです。もしくは[公式ページ](https://test-utils.vuejs.org/api/#vm)で説明されてる「vm」を上手く使う事になりそうです。

### 結果チェック（expect）

jestの公式ページに各種関数のリストがありました。サンプルで使用されていた`toHaveLength`も記載されています。

https://jestjs.io/ja/docs/expect


# テスト作成

当然ながら、UIのテストをするのに十分な命令がそろっていると思いました。自分の作ったコンポーネントに対してテストケースを作成してみます。実際にはいろいろなケースを作りますが、この記事では以下のテストをするテストケースを作りたいと思います。

- 指定してある手数に基づいてスクランブルした時、操作履歴数がその手数と同じになるか
- ３手戻した時、内部で持っているステータスが３手分戻っているか

## 実行エラー解消

使ってるコンポーネントと導入してテスト作成したら、以下のエラーが出てしまいました。先ほどのダミープロジェクトからソースとテストケースを持ってきても同じエラーが出ます。ネットを調べると、バージョンの組み合わせが関係していそうです。そういえば必要モジュールをインストールした時、微妙な順番になっていたと思います。

```console
  ● Test suite failed to run

    TypeError: babelJest.getCacheKey is not a function

      at Object.getCacheKey (node_modules/@vue/vue3-jest/lib/index.js:13:19)
      at ScriptTransformer._getCacheKey (node_modules/@jest/transform/build/ScriptTransformer.js:281:41)
      at ScriptTransformer._getFileCachePath (node_modules/@jest/transform/build/ScriptTransformer.js:352:27)
      at ScriptTransformer.transformSource (node_modules/@jest/transform/build/ScriptTransformer.js:595:32)
      at ScriptTransformer._transformAndBuildScript (node_modules/@jest/transform/build/ScriptTransformer.js:765:40)
      at ScriptTransformer.transform (node_modules/@jest/transform/build/ScriptTransformer.js:822:19)
```

2022年5月15日現在で、先ほどのダミープロジェクトのpackage.jsonでのバージョンを参考に、使用している側のpakage.jsonのバージョンを以下の通りに修正しました。まだVue3用のテストは発展途中と思われ組み合わせによる不具合がありそうです。とりあえずこのバージョンで進めていきます。

```jsonc:package.json
  // 関係部分のみ抜粋
  "devDependencies": {
    "@types/jest": "^27.5.1",
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-eslint": "~5.0.0",
    "@vue/cli-plugin-typescript": "~5.0.0",
    "@vue/cli-plugin-unit-jest": "^5.0.0",
    "@vue/test-utils": "^2.0.0-0",
    "@vue/vue3-jest": "^27.0.0-alpha.1",
    "babel-jest": "^27.0.6",
    "eslint": "^7.32.0",
    "eslint-plugin-vue": "^8.0.3",
    "jest": "^27.0.5",
    "ts-jest": "^27.0.4",
    "typescript": "~4.5.5",
  }

```

その後以下の手順を踏みました。

- `node_modules`以下全削除
- `yarn.lock`削除
- `yarn instal`実行

そして、`yarn jest`したらテスト実体が何もないテストケース（mountでのレンダリング処理のみ）が成功する様になりました。


## wasmモジュール読み込みエラー

何かやり方あるかもしれませんが、wasmモジュールを読み込むところでエラーが出てしまいました。package.jsはwasmのコンパイルモジュールなので基本触れないファイルです。一旦wasmモジュールを組み込んでいるコンポーネントは諦める事にします。

※後に解消しました。拙記事「[jestでAPIなど他リソースが必要な機能のテストをしてみる](https://qiita.com/etnk/items/b97055ac5faca35611b2)」

```console
    /vue/cubetrain/src/wasm/package.js:352
        input = new URL('package_bg.wasm', import.meta.url);
                                                  ^^^^

    SyntaxError: Cannot use 'import.meta' outside a module
```

## vuetifyのコンポーネントレンダリングでワーニング

別の基本的なコンポーネントで試してみます。
`compilerOptions.isCustomElement`を設定すれば良さそうなメッセージが出てきます。

```
    console.warn
      [Vue warn]: Failed to resolve component: v-container
      If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
        at <ControlPanel defspeed=40 defscramblestep=20 ref="VTU_COMPONENT" > 
        at <VTUROOT>
```

しかし、詳細には記載しませんが、[関係していそうなGithubのissue](https://github.com/vuejs/vue-jest/issues/389)を参考に色々やってみましたが駄目でした。

ElementUIという別のUIライブラリで同じようなケースがある様です。
[How to solve ElementUI unknown element warning in unit tests?](https://stackoverflow.com/questions/71028201/how-to-solve-elementui-unknown-element-warning-in-unit-tests/71029817#71029817)

### vuetifyの公式ページ確認

vuetify2.6.5の公式ページのGetting Startedブロックには[Unit Testing](https://vuetifyjs.com/en/getting-started/unit-testing/)があります。
Vuetify3.0.0Betaの公式ページの[Getting Started](https://next.vuetifyjs.com/en/getting-started/installation/)ブロックにはなさそうです。
現時点ではVuetify関係のテストは出来ないと思って良さそうです。なんとなくこれがまだVuetifyがβ版である最大の理由では無いかと思います。

### 試しにやってみる

レンダリングが出来なければボタンなどDOM情報を取得出きず、当然そのイベント発火が出来ないので諦めかけていました。
ただ、出ていたメッセージがWarningだったのでちょっとやってみようと思いました。
すると、Warningは出るものの、テスト自体は成功しました。

```typescript:操作パネルクラスのテストケース
import { shallowMount } from '@vue/test-utils'
import ControlPanel from '@/components/ControlPanel.vue'

describe('Test ControlPanel.vue', () => {

  // clickイベントでawaitを使うので、asyncを指定します。
  it('success to fire scramble', async () => {
    const wrapper = shallowMount(ControlPanel, {
      props: {
        defspeed: 40,
        defscramblestep: 20
      }
    })
    expect(wrapper).toBeDefined();

    // ボタンには「controlpanel_scramble」クラスが指定してあります。
    const scramblebtn = wrapper.find('.controlpanel_scramble');
    expect(scramblebtn).toBeDefined();
    // clickイベントを発火します
    await scramblebtn.trigger('click')

    console.log(wrapper.emitted().controlAction[0]);
    // ボタンclickでemitが発生するのでその中身をチェックします。
    // { controlAction: [ [ 'scramble', 20 ] ], click: [ [ [MouseEvent] ] ] }
    expect(wrapper.emitted().controlAction[0]).toStrictEqual(['scramble', 20]);
  })
})
```

## 本体モジュールのテストケース作成時に発生したエラー

### import.meta.urlでエラー

```console
  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /vue/cubetrain/src/wasm/package.js:356
        input = new URL('package_bg.wasm', import.meta.url);
                                                  ^^^^

    SyntaxError: Cannot use 'import.meta' outside a module

      4 | <script lang="ts">
      5 | import { defineComponent, toRefs, onMounted, ref } from 'vue';
    > 6 | import init, { start } from '@/wasm/package.js';
        | ^
      7 |
      8 | export default defineComponent({
      9 |   name: "WasmScreen",
```

こんなエラーが出てきました。もちろんトップのコンポーネントはwasmモジュールを子コンポーネントとして含むので、そのインターフェースである所のpackage.jsファイルでエラーが出ているという状況の様です。先ほどは諦めましたが、やりたい所です。

エラーメッセージから似たケースを探していたら以下のページにたどり着きました。package.jsの`import.meta.url`を`import_meta_url`に変更してみます。

https://tagmerge.com/question/how-to-use-import-meta-when-testing-with-jest

### TextDecoderでエラー

import_meta_urlで変更したら別のエラーが出てきました。TextDecoderが無いとかの話です。

```
  ● Test suite failed to run

    ReferenceError: TextDecoder is not defined

      10 | // }
      11 |
    > 12 | let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
         |                         ^
      13 |
      14 | cachedTextDecoder.decode();
```

こちらのページが参考になりました。

https://qiita.com/Domao/items/fdd982ec9b8b7d095eda

`yarn add util`実行実行した後、package.jsの冒頭に以下を記載する事で対応しました。  
※記事ではsetupJest.tsに記載しています。

```javascript
if (typeof global.TextEncoder === 'undefined') {
    global.TextEncoder = require('util').TextEncoder
}
if (typeof global.TextDecoder === 'undefined') {
    global.TextDecoder = require('util').TextDecoder
}
```


### fetchでエラー

同じようなエラーが出てきました。下記ページが参考になりました。

https://blog.unresolved.xyz/jest-fetch-is-not-defined

`yarn add -D whatwg-fetch`実行して、package.jsの冒頭に以下を記載する事で対応しました。


```javascript
if (typeof global.fetch === 'undefined') {
    const fetchPolifill = require('whatwg-fetch')
    global.fetch = fetchPolifill.fetch
}
```

### wasm関数の読み込みでエラー

undefinedエラーが出てしまった部分があり、package.jsの該当部分でif文で回避しました。この関数を使用しているアニメーション部分では正常にテスト出来なくなるのですが、一旦先に進みます。

```javascript
export function on_animation() {
    if (wasm) {
        var ret = wasm.on_animation();
        return ret;
    }
}
```

## 本体モジュールのテストケース作成

これでようやく本題に入れます。先に結果から記載します。

```typescript
import { mount } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import App from '@/App.vue'

describe('Test App.vue', () => {

  it('success to fire scramble and number of history', async () => {
    const wrapper = mount(App, {
      props: {}
    })
    expect(wrapper).toBeDefined();

    const STEP = 10;

    // set speed
    const speed_input = wrapper.find('.controlpanel_speedval');
    expect(speed_input).toBeDefined();
    speed_input.setValue(100);
    const speed_btn = wrapper.find('.controlpanel_speed');
    expect(speed_btn).toBeDefined();
    await speed_btn.trigger('click');

    // set valute to step
    const scramblestep = wrapper.find('.controlpanel_scramblestep');
    expect(scramblestep).toBeDefined();
    scramblestep.setValue(STEP);

    // fire scramble
    const scramblebtn = wrapper.find('.controlpanel_scramble');
    expect(scramblebtn).toBeDefined();
    await scramblebtn.trigger('click');

    // check number of steps
    expect(wrapper.vm.rotateStepList.length).toBe(STEP);
    await flushPromises();

    // check playback status
    const playbackbtn = wrapper.find('.app_playbackonestep');
    expect(playbackbtn).toBeDefined();
    await playbackbtn.trigger('click');
    await playbackbtn.trigger('click');
    await playbackbtn.trigger('click');
    await flushPromises();
    expect(wrapper.vm.rotateStepList[STEP - 4].rotateStatus).toBe("done");
    expect(wrapper.vm.rotateStepList[STEP - 3].rotateStatus).toBe("bef");
  })
})
```

### ポイント

事前にある程度知識を整理したつもりですが、実際にはまったポイントがいくつかありました。

#### Vueインスタンスの変数

今回のケースでは、内部で保持されているステータスを取得する必要がありました。それを表示するDOM要素を頑張って取得しても良いですが、内部変数でチェックしたい所です。上でちょっとチェックした「vm」を使って`wrapper.vm.rotateStepList`の様に指定して取得出来ました。

```vue:本体側の記載
  // 中略
  setup(){
    // 中略
    const rotateStepList = ref<Array<RotateStep>>(stepManager.getCurrentStepList());
    // 中略
  }
})
```

#### 非同期処理

Promise系処理が終わっていないケースがあるので、`await flushPromises()`で待つ必要があります。

```console
yarn add --dev flush-promises
```

#### 他のコンポーネントをラッピングする場合

ここまできてもハマった点があったので共有しておきます。vuetifyの`v-tooltip`を使ったケースです。
修正前の書き方だと、wrapper.findでDOMが取得できません。どうやら、tooltipコンポーネントの修正前の書き方だとjestでmountした時にDOMとして存在しない様です。v-btnが表に来る書き方に修正して成功しました。
別のコンポーネントでも同じようなケースがあると思います。

```vue:修正前
          <v-tooltip anchor="start">
            <template v-slot:activator="{ props }">
              <v-btn v-bind="props" width="30" flat @click="onPlaybackOneStep" class="app_playbackonestep">
                <v-icon>mdi-chevron-left</v-icon>
              </v-btn>
            </template>
            <div>１ステップ戻します</div>
          </v-tooltip>
```

```vue:修正後
          <v-btn v-bind="props" width="30" flat @click="onPlaybackOneStep" class="app_playbackonestep">
            <v-icon>mdi-chevron-left</v-icon>
            <v-tooltip activator="parent" anchor="start"><div>１ステップ戻します</div></v-tooltip>
          </v-btn>
```


# 残問題

色々package.jsに修正を入れましたが、このファイルはwasmのビルドモジュールです。基本的に修正したくありません。
lint対応の為、冒頭に`/* eslint-disable */`を入れる処理をしていたので、その処理に組み込む事にします。
多分将来的に整備されていくと思うので、その時には外したいと思います。

また、wasmモジュールの読み込みがやっぱりうまく行ってません。どうやらfetchで失敗しているようです。[自分の以前の記事](https://qiita.com/etnk/items/3541e492862770c17341)でもnginxを使って読み込んでいたのでした。
今回の目的と外れそうなので、これに関してはまた別の記事で頑張ってみたいと思います。

```
    TypeError: Network request failed

      at node_modules/whatwg-fetch/dist/fetch.umd.js:535:18
      at Timeout.task [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:516:19)
```

# その他

公式ページからリンクされていたYouTube動画がありました。今後深い事をする場面が出てくると思うので、一通り見ておきたい所です。英語ですけど。

https://www.youtube.com/playlist?list=PLC2LZCNWKL9ahK1IoODqYxKu5aA9T5IOA