TypeScript で実装した Nuxt.js/Store をテストしたい
成果物
出来上がりはこちらに
https://github.com/hirose504/nuxt-typescript-vuex-module-decorators-test
準備
create nuxt-app
$ yarn create nuxt-app nuxt-typescript-vuex-module-decorators-test
yarn create v1.21.1
create-nuxt-app v2.14.0
✨ Generating Nuxt.js project in nuxt-typescript-vuex-module-decorators-test
? Project name nuxt-typescript-vuex-module-decorators-test
? Project description My posh Nuxt.js project
? Author name hirose504
? Choose the package manager Yarn
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Choose linting tools ESLint, Prettier
? Choose test framework Jest
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)
🎉 Successfully created project nuxt-typescript-vuex-module-decorators-test
To get started:
cd nuxt-typescript-vuex-module-decorators-test
yarn dev
To build & start for production:
cd nuxt-typescript-vuex-module-decorators-test
yarn build
yarn start
To test:
cd nuxt-typescript-vuex-module-decorators-test
yarn test
✨ Done in 182.69s.
typescript setup
https://typescript.nuxtjs.org/ja/guide/setup.html
https://typescript.nuxtjs.org/ja/guide/runtime.html
https://typescript.nuxtjs.org/ja/guide/lint.html
add vuex-module-decorators
$ yarn add vuex-module-decorators
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,7 @@
"esnext.asynciterable",
"dom"
],
+ "experimentalDecorators": true,
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
add ts-jest
$ yarn add --dev ts-jest @types/jest
--- a/jest.config.js
+++ b/jest.config.js
@@ -4,14 +4,16 @@ module.exports = {
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js'
},
- moduleFileExtensions: ['js', 'vue', 'json'],
+ moduleFileExtensions: ['ts', 'js', 'vue', 'json'],
transform: {
+ '^.+\\.ts$': 'ts-jest',
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest'
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
- '<rootDir>/pages/**/*.vue'
+ '<rootDir>/pages/**/*.vue',
+ '<rootDir>/store/**/*.ts'
]
}
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -26,7 +26,8 @@
},
"types": [
"@types/node",
- "@nuxt/types"
+ "@nuxt/types",
+ "@types/jest"
]
},
"exclude": [
実装
https://ja.nuxtjs.org/guide/vuex-store/ をベースに TypeScript で書く
types
~/types/index.d.ts
export type Todo = {
text: string
done: boolean
}
store
~/store/todos.ts
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
import { Todo } from '~/types'
@Module({ name: 'todos', stateFactory: true, namespaced: true })
export default class TodosModule extends VuexModule {
list: Todo[] = []
@Mutation
add(text: string) {
this.list.push({
text,
done: false
})
}
@Mutation
remove(todo: Todo) {
this.list.splice(this.list.indexOf(todo), 1)
}
@Mutation
toggle(todo: Todo) {
todo.done = !todo.done
}
}
~/store/index.ts
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'
~/utils/store-accessor.ts
/* eslint-disable import/no-mutable-exports */
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import TodosModule from '~/store/todos'
let todosStore: TodosModule
function initialiseStores(store: Store<any>): void {
todosStore = getModule(TodosModule, store)
}
export { initialiseStores, todosStore }
pages
~/pages/todos.vue
<template>
<ul>
<li v-for="todo in todos" :key="todo.index">
<input type="checkbox" :checked="todo.done" @change="toggle(todo)" />>
<span :class="{ done: todo.done }">{{ todo.text }}</span>
</li>
<li>
<input placeholder="What needs to be done?" @keyup.enter="addTodo" />
</li>
</ul>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
todos() {
return this.$store.state.todos.list
}
},
methods: {
addTodo(e) {
this.$store.commit('todos/add', e.target.value)
e.target.value = ''
},
...mapMutations({
toggle: 'todos/toggle'
})
}
}
</script>
<style>
.done {
text-decoration: line-through;
}
</style>
テスト
~/test/store/todos.test.ts
import { createStore } from '~/.nuxt/store'
import { initialiseStores, todosStore } from '~/utils/store-accessor'
describe('TodosModule', () => {
beforeEach(() => {
initialiseStores(createStore())
})
test('init', () => {
expect(todosStore.list).toEqual([])
})
test('add', () => {
todosStore.add('test')
expect(todosStore.list).toEqual([{ text: 'test', done: false }])
})
test('remove', () => {
todosStore.add('aaaa')
todosStore.add('bbbb')
todosStore.add('cccc')
todosStore.remove(todosStore.list[1])
expect(todosStore.list).toEqual([
{ text: 'aaaa', done: false },
{ text: 'cccc', done: false }
])
})
test('toggle', () => {
todosStore.add('aaaa')
todosStore.add('bbbb')
todosStore.add('cccc')
todosStore.toggle(todosStore.list[1])
expect(todosStore.list).toEqual([
{ text: 'aaaa', done: false },
{ text: 'bbbb', done: true },
{ text: 'cccc', done: false }
])
})
})
実行
$ yarn test master
yarn run v1.21.1
$ jest
PASS test/store/todos.test.ts
PASS test/Logo.spec.js
------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files | 70.83 | 0 | 60 | 66.67 | |
components | 100 | 100 | 100 | 100 | |
Logo.vue | 100 | 100 | 100 | 100 | |
pages | 0 | 0 | 0 | 0 | |
index.vue | 0 | 100 | 0 | 0 | 1,28 |
todos.vue | 0 | 0 | 0 | 0 | 1,14,19,24,25 |
store | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
todos.ts | 100 | 100 | 100 | 100 | |
------------|----------|----------|----------|----------|-------------------|
Test Suites: 2 passed, 2 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 5.098s
Ran all test suites.
✨ Done in 8.17s.