VueScrollToはdirectivesで渡す

VueScrollTo を使っているコンポーネントのテストを書くときに躓いたので、その時の対応をまとめました。

# その時の状況

Vueコンポーネントのテストを書きはじめたところでした。
とりあえず最低限のテストを書きます。
componentをmountして、htmlを出力してみます。

```
import { mount } from "@vue/test-utils";
import Complete from "@/views/Complete.vue";

const $store = {
  state: {
    answer: {
      type: "hoge",
    },
  },
};

describe("Complete.vue", () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(Complete, {
      global: {
        mocks: {
          $store,
        },
      },
    });
  });
  it("ページが表示される", async () => {
    console.log(wrapper.html());
  });
});

```

このテストを実行したところ、失敗してしまいました。

```
FAIL  tests/unit/Complete/template.spec.js
  Complete.vue
    ✕ ページが表示される (49 ms)

  ● Complete.vue › ページが表示される

    TypeError: Cannot read property 'created' of undefined

      14 | 
      15 |   beforeEach(() => {
    > 16 |     wrapper = mount(Complete, {
         |               ^
      17 |       global: {
      18 |         mocks: {
      19 |           $store,

      at invokeDirectiveHook (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:2922:33)
      at mountElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3814:17)
      at processElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3793:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3713:21)
      at mountChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3888:13)
      at mountElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3811:17)
      at processElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3793:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3713:21)
      at mountChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3888:13)
      at mountElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3811:17)
      at processElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3793:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3713:21)
      at mountChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3888:13)
      at mountElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3811:17)
      at processElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3793:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3713:21)
      at mountChildren (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3888:13)
      at mountElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3811:17)
      at processElement (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3793:13)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3713:21)
      at componentEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4204:21)
      at reactiveEffect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:46:24)
      at Object.effect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:21:9)
      at setupRenderEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4169:38)
      at mountComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4128:9)
      at processComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4088:17)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3716:21)
      at componentEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4204:21)
      at reactiveEffect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:46:24)
      at Object.effect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:21:9)
      at setupRenderEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4169:38)
      at mountComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4128:9)
      at processComponent (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4088:17)
      at patch (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3716:21)
      at render (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4789:13)
      at mount (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:3054:25)
      at Object.app.mount (node_modules/@vue/runtime-dom/dist/runtime-dom.cjs.js:1232:23)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.cjs.js:2335:18)
      at Object.<anonymous> (tests/unit/Complete/template.spec.js:16:15)

  ● Complete.vue › ページが表示される

    TypeError: Cannot read property 'html' of undefined

      23 |   });
      24 |   it("ページが表示される", async () => {
    > 25 |     console.log(wrapper.html());
         |                         ^
      26 |   });
      27 | });
      28 | 

      at Object.<anonymous> (tests/unit/Complete/template.spec.js:25:25)

  console.warn
    [Vue warn]: Failed to resolve directive: scroll-to 
      at <Complete ref="VTU_COMPONENT" > 
      at <VTUROOT>

      60 |             src="/uploads/images/thanks01.jpg"
      61 |           />
    > 62 |           <img
         |                                   ^
      63 |             src="/uploads/images/thanks02.jpg"
      64 |           />
      65 |           <img

      at warn (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:40:17)
      at resolveAsset (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5133:13)
      at Object.resolveDirective (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5104:12)
      at Proxy.render (src/views/Complete.vue:62:38)
      at renderComponentRoot (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:729:44)
      at componentEffect (node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4186:53)
      at reactiveEffect (node_modules/@vue/reactivity/dist/reactivity.cjs.js:46:24)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        3.4 s
Ran all test suites matching /\/Users\/okada\/src\/front\/tests\/unit\/Complete\/template.spec.js/i with tests matching "Complete\.vue".
```

しかし、エラーメッセージが謎です…

```
TypeError: Cannot read property 'created' of undefined
```

``Complete.vue``では、``created``使ってないんだけどな :thinking: 

# やったこと

## 問題箇所の特定

エラーメッセージからは読み解けないため、``Complete.vue``の中身を変えて問題の箇所を特定しました。具体的には、``Complete.vue``の中から怪しそうなところを消して実行→通ったらその場所が問題なので、更に狭い範囲を消して実行、を繰り返し、以下の場所に問題があることがわかりました。

```
    <div class="present-area-btn">
      <ul>
        <li>
          <a href="#" v-scroll-to="'#present-area'">
            <img
              src="/uploads/images/button.png"
            />
          </a>
        </li>
        <li>
          <a href="#" v-scroll-to="'#other-area'">
            <img src="/uploads/images/other.png" />
          </a>
        </li>
      </ul>
    </div>
```

``v-scroll-to``とありますね。

``v-scroll-to``は、[vue-scrollto](https://github.com/rigor789/vue-scrollto)というパッケージのもののようです。
以下のページによると、いい感じにスクロールしてくれるパッケージだそうです。

https://www.kabanoki.net/4361/


src/main.jsを確認すると、確かに使っていました。

```
app
  .use(router)
  .use(store)
  .use(VueAxios, axios)
  .use(VueScrollTo, {
    offset: -100,
  })
```

そして、テスト失敗時のメッセージをもう一度よく見ると、warnも出ていました :sweat_drops:

```
    [Vue warn]: Failed to resolve directive: scroll-to 
```

``scroll-to``という``directive``が解決できないと言っていました。

## 対応内容と結果

ということで、``mount``時に``directive``を渡してみました。

```
directives: {
  scrollTo: VueScrollTo,
},
```

テスト全文

```
import { mount } from "@vue/test-utils";
import VueScrollTo from "vue-scrollto";
import Complete from "@/views/Complete.vue";

const $store = {
  state: {
    answer: {
      type: "hoge",
    },
  },
};

describe("Complete.vue", () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(Complete, {
      global: {
        mocks: {
          $store,
        },
        directives: {
          scrollTo: VueScrollTo,
        },
      },
    });
  });
  it("ページが表示される", async () => {
    console.log(wrapper.html());
  });
});

```

結果：成功しました 🎉

```
 PASS  tests/unit/Complete/template.spec.js
  Complete.vue
    ✓ ページが表示される (68 ms)

  console.log
    <div class="host"><div class="comp-top-area"><img src="/uploads/images/thanks_mv.png"> …

      at Object.<anonymous> (tests/unit/Complete/template.spec.js:29:13)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.814 s
Ran all test suites matching /\/Users\/okada\/src\/front\/tests\/unit\/Complete\/template.spec.js/i with tests matching "Complete\.vue".
```

# 補足

[github検索](https://github.com/search?q=VueScrollTo+%7B+filename%3A%7B%2A.spec.%2A%7D.&type=code
)すると、``Vue.use(VueScrollTo)``でやってる人が多そうでした。Vue.useはVue2の書き方なので、それをVue3の書き方にすればよかっただけなのかもしれない…？（試してない）

```
import VueScrollTo from "vue-scrollto”;
import { createApp } from "vue”;
const app = createApp({});
app.use(VueScrollTo, {
  offset: -100,
});
```