Part1 ここ
Part2 https://qiita.com/senku/items/20e21033edd512be1d4d
Part3 https://qiita.com/senku/items/08d547eda2c6ff818108
Vue.jsでビジュアルリグレッションテストをするためにいろいろやったナレッジを公開しまーす。
やりたいこと
- Vue.jsのコンポーネントのビジュアルリグレッションテスト(画像回帰テスト)をしたい。
- viewsレベルのテストもしたい。(Vuex/Vue Routerが絡む)
- 多言語対応もしたい。(Vue I18nが絡む)
- テストはなるべくローカルで完結したい。サーバ立てたりしたくない。
Summary
Storybook(ビジュアルの元ネタ) + storycap(画像生成) + reg-suit(画像比較&レポート) でやる。
Storyを作るために悪戦苦闘する
- Vue.js開発環境の構築
- StorybookでコンポーネントのStoryをつくろう
- Vue I18nと戦う
- Vue Routerはかんたん(ここまで)
- Vuexはモックする
- Storycapで画像を生成
- reg-suitで比較&レポート
Vue.jsのセットアップ
ゼロからやりますと言ったものの、みんな流石にNode.jsは持ってるよね。
Vue CLI いれる
公式のインストールに従います。
$ npm install -g @vue/cli
(略)
+ @vue/cli@4.1.1
$ vue --version
@vue/cli 4.1.1
Vue CLI 4だ!
プロジェクトをつくる
公式のプロジェクトの作成に従う。はろーわーるど!
$ vue create hello-world
とりあえずデフォルトでいきましょう。babelとESLintだけ。
? Please pick a preset: (Use arrow keys)
❯ default (babel, eslint)
言われるがままにcd
してnpm run serve
します。
$ cd hello-world
$ npm run serve
http://localhost:8080 ではろーわーるどできましたね。
Storybook導入
Storybookセットアップ
Storybook for Vueは見なかったことにして、Vue CLI plugin for Storybookの手順で入れます。Vue CLI Pluginとしてセットアップしないとvue.config.js
を参照してくれないので後で詰みます(1敗)。Vue CLIに逆らってはならぬ。
$ vue add storybook
Initial Framework
には無言でEnterを押します。
インストール後、言われるがままにnpm run storybook:serve
します。
$ npm run storybook:serve
自動でブラウザが起動して、http://localhost:6006/でStorybookが表示されますね。
かなり後で使うので、ブラウザが起動しないコマンドもここで作っておきましょう。--ci
オプションが使えます。
オプションにはStorybookのCLI Optionがそのまま渡せるので覚えておくといつか役に立つかもしれません。
diff --git a/package.json b/package.json
index dfba14f..533efd0 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"storybook:build": "vue-cli-service storybook:build -c config/storybook",
- "storybook:serve": "vue-cli-service storybook:serve -p 6006 -c config/storybook"
+ "storybook:serve": "vue-cli-service storybook:serve -p 6006 -c config/storybook",
+ "storybook:ci": "vue-cli-service storybook:serve -p 6006 -c config/storybook --ci"
},
"dependencies": {
"core-js": "^3.4.3",
HelloWorldコンポーネントのStoryを書いてみる
Storyの書き方はいろいろありますが、とりあえずこんな感じで作ってみます。
import { storiesOf } from "@storybook/vue";
import HelloWorld from "@/components/HelloWorld.vue";
storiesOf("HelloWorld", module)
.add("test", () => {
return {
components: { HelloWorld },
template: `
<hello-world msg="from storybook" />
`
};
});
Storybookに表示されますね。msg
で渡したfrom storybook
もちゃんと表示されています。
ここから先しばらくは、Storybookと他のプラグインとの兼ね合いの話をします。
Vue I18nとのたたかい
Storybook で Vue-I18nを使う場合はちょっと細工が必要です。
まずは vue-i18nをセットアップします。公式のInstallation参照。
Vue CLI 3.x向けの手順ですがたぶん問題ありません。
$ vue add i18n
とりあえず全部デフォルトで。基本のロケールがen
になります。
? The locale of project localization. en
? The fallback locale of project localization. en
? The directory where store localization messages of project. It's stored under `src` directory. locales
? Enable locale messages in Single file components ? No
src/main.js
からsrc/i18n.js
がロードされるようになりました。
localeのファイルがsrc/locales/en.json
にできています。
{
"message": "hello i18n !!"
}
ちょっとsrc/components/HelloWorld.vue
から読み込ませてみます。
diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue
index 879051a..e77721a 100644
--- a/src/components/HelloWorld.vue
+++ b/src/components/HelloWorld.vue
@@ -1,6 +1,7 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
+ <p>{{ $t('message') }}</p>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
npm run serve
して http://localhost:8080/ を見てみます。
hello i18n !!
が表示されてますね。
多言語の対応はここでは置いておいて、とりあえずen
で話を進めます。
Vue I18n × Storybook
StorybookでHelloWorldのStoryを見てみましょう。npm run storybook:serve
で再ロードさせます。
残念なことにエラーが出ます。Vue I18nがセットするプロパティの$t
がないからですね。
Storybookの起動は src/main.js
を経由していないため、別途コンポーネントにVueI18nを紐付けてやる必要があります。
やり方はいろいろあるんですが、とりあえずデコレータで解決しています。Vue.use
はいらない。
import i18n from "@/i18n";
export default function defaultDecorator() {
return {
template: "<div><story /></div>",
i18n
};
}
diff --git a/src/stories/HelloWorld.stories.js b/src/stories/HelloWorld.stories.js
index 93b4b7e..d8e8605 100644
--- a/src/stories/HelloWorld.stories.js
+++ b/src/stories/HelloWorld.stories.js
@@ -1,7 +1,9 @@
import { storiesOf } from "@storybook/vue";
+import defaultDecorator from "@/stories/defaultDecorator";
import HelloWorld from "@/components/HelloWorld.vue";
storiesOf("HelloWorld", module)
+ .addDecorator(defaultDecorator)
.add("test", () => {
return {
components: { HelloWorld },
エラーが解消して、hello i18n !!
が表示されます。
vue-i18n-loaderとのたたかい
Vue I18nには、SFCの中に定義を書けるvue-i18n-loaderがあります。これを使う場合は更にひと手間が必要です。
とりあえずvue invoke i18n
で、プラグインの設定をvue-i18n-loaderが使えるように更新します。
$ vue invoke i18n
? The locale of project localization. en
? The fallback locale of project localization. en
? The directory where store localization messages of project. It's stored under `src` directory. locales
? Enable locale messages in Single file components ? Yes // ここだけy!
vue-i18n-loader
が追加され、vue.config.js
のenableInSFC
がtrueになり、src/components/HelloI18n.vue
が作成されます。
App.vue
から読めるようにしましょう。
diff --git a/src/App.vue b/src/App.vue
index fcc5662..0660f5b 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,16 +1,19 @@
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
+ <HelloI18n/>
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
+import HelloI18n from './components/HelloI18n.vue'
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
components: {
+ HelloI18n,
HelloWorld
}
}
npm run serve
を再実行すると、Hello i18n in SFC!
が表示されます。(画像はテキスト選択しちゃった)
vue-i18n-loader × Storybook
src/components/HelloI18n.vue
のStoryを作ります。
import { storiesOf } from "@storybook/vue";
import defaultDecorator from "@/stories/defaultDecorator";
import HelloI18n from "@/components/HelloI18n.vue";
storiesOf("HelloI18n", module)
.addDecorator(defaultDecorator)
.add("test", () => {
return {
components: { HelloI18n },
template: `
<hello-i18n />
`
};
});
この時点ではStorybookは正しく動作しません。keyであるhello
がそのまま表示されてしまいます。
Storybookとvue-i18n-loaderの間には複雑な問題があります。
いろいろと見守ってはいるんですが、decoratorでthis.$root._i18n
をセットするのが現状可能な解決策の一つです。(via. dlucian/vuejs-storybook-i18n #1)ただし強引な解決策なので、いつ動かなくなるかわかんないです。
diff --git a/src/stories/defaultDecorator.js b/src/stories/defaultDecorator.js
index 4e5acdf..c50bf20 100644
--- a/src/stories/defaultDecorator.js
+++ b/src/stories/defaultDecorator.js
@@ -3,6 +3,9 @@ import i18n from "@/i18n";
export default function defaultDecorator() {
return {
template: "<div><story /></div>",
- i18n
+ i18n,
+ beforeCreate: function() {
+ this.$root._i18n = this.$i18n;
+ }
};
}
Hello i18n in SFC!
が表示されるようになりました。
Vue Routerとのたたかい
Routerを入れ忘れていたので入れます。
$ vue add router
Use history mode for router? (Requires proper server setup for index fallback in production)
には無言でEnter(Yes
)を押していきます。
Vue Routerをインストールすると、src/App.vue
からsrc/views/About.vue
とsrc/views/Home.vue
へのリンクが生成されます。というかファイルごと上書きされます。さようならHelloI18n。
src/App.vue
のStoryを書いてみます。
import { storiesOf } from "@storybook/vue";
import defaultDecorator from "@/stories/defaultDecorator";
import App from "@/App.vue";
storiesOf("App", module)
.addDecorator(defaultDecorator)
.add("test", () => {
return {
components: { App },
template: `
<app />
`
};
});
StorybookでこのStoryを表示するとエラーが起こるので、decoratorで解決します。
Storybookでページ遷移させる予定はないので、ルーティング情報は空にしておきます。すまんな。
diff --git a/src/stories/defaultDecorator.js b/src/stories/defaultDecorator.js
index c50bf20..746aef7 100644
--- a/src/stories/defaultDecorator.js
+++ b/src/stories/defaultDecorator.js
@@ -1,8 +1,10 @@
+import Router from 'vue-router'
import i18n from "@/i18n";
export default function defaultDecorator() {
return {
template: "<div><story /></div>",
+ router: new Router({}),
i18n,
beforeCreate: function() {
this.$root._i18n = this.$i18n;
config/storybook/config.js
でVue.use
しておく必要もあります。
diff --git a/config/storybook/config.js b/config/storybook/config.js
index fe20bab..d8dcd31 100644
--- a/config/storybook/config.js
+++ b/config/storybook/config.js
@@ -1,5 +1,9 @@
/* eslint-disable import/no-extraneous-dependencies */
import { configure } from '@storybook/vue'
+import Vue from 'vue'
+import Router from 'vue-router'
+
+Vue.use(Router)
const req = require.context('../../src/stories', true, /.stories.js$/)
ここまでやれば、Vue Routerを使うコンポーネントのStoryが表示できるようになります。
リンクを押しても何も起こりません。