Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3c33dc3

Browse files
authored
feat: Remove Router/Vuex for Vue 3 (#206)
* Remove handling of router/vuex * Fix existing tests * Mark types as deprecated * Add simple vuex example * Improve messaging * Mark store as optional
1 parent aa00f27 commit 3c33dc3

File tree

9 files changed

+168
-131
lines changed

9 files changed

+168
-131
lines changed
Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
1-
<template>
2-
<div>
3-
<h2>Counter</h2>
4-
<div>
5-
<button data-testid="decrementer" @click="decrement">-</button>
6-
<span data-testid="count-value">{{ count }}</span>
7-
<button data-testid="incrementer" @click="increment">+</button>
8-
</div>
9-
</div>
10-
</template>
11-
12-
<script>
13-
import { mapActions, mapState } from 'vuex'
14-
15-
export default {
16-
computed: {
17-
...mapState(['count'])
18-
},
19-
20-
methods: {
21-
...mapActions(['decrement', 'increment'])
22-
}
23-
}
24-
</script>
1+
<template>
2+
<h2>Counter</h2>
3+
<button data-testid="decrementer" @click="decrement">-</button>
4+
<span data-testid="count-value">{{ count }}</span>
5+
<button data-testid="incrementer" @click="increment">+</button>
6+
</template>
7+
8+
<script>
9+
import {mapActions, mapState} from 'vuex'
10+
11+
export default {
12+
computed: {
13+
...mapState(['count']),
14+
},
15+
16+
methods: {
17+
...mapActions(['decrement', 'increment']),
18+
},
19+
}
20+
</script>

‎src/__tests__/deprecated-options.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import {render} from '..'
2+
3+
beforeEach(() => {
4+
jest.spyOn(console, 'warn').mockImplementation(() => {})
5+
})
6+
7+
afterEach(() => {
8+
console.warn.mockRestore()
9+
})
10+
11+
test('warns on deprecated store option', () => {
12+
const Component = {template: `<div></div>`}
13+
14+
render(Component, {
15+
store: 'anything',
16+
})
17+
18+
expect(console.warn).toHaveBeenCalledTimes(1)
19+
expect(console.warn).toHaveBeenCalledWith(
20+
expect.stringContaining(
21+
`Providing 'store' or 'routes' options is no longer available`,
22+
),
23+
)
24+
})
25+
26+
test('warns on deprecated routes option', () => {
27+
const Component = {template: `<div></div>`}
28+
29+
render(Component, {
30+
routes: 'anything',
31+
})
32+
33+
expect(console.warn).toHaveBeenCalledTimes(1)
34+
expect(console.warn).toHaveBeenCalledWith(
35+
expect.stringContaining(
36+
`Providing 'store' or 'routes' options is no longer available`,
37+
),
38+
)
39+
})

‎src/__tests__/plugins.js

Lines changed: 0 additions & 49 deletions
This file was deleted.

‎src/__tests__/vue-router-mocha.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

‎src/__tests__/vue-router.js

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
1-
// Please notice that this example is a draft example on how to test
2-
// the router.
3-
// Related issue on Vue Test Utils: https://github.com/vuejs/vue-test-utils-next/issues/152
1+
/* eslint-disable vue/require-prop-types */
2+
/* eslint-disable vue/one-component-per-file */
43

54
import '@testing-library/jest-dom'
65
import {waitFor} from '@testing-library/dom'
6+
import {defineComponent} from 'vue'
7+
import {createRouter, createWebHistory} from 'vue-router'
78
import {render, fireEvent} from '..'
89
import App from './components/Router/App.vue'
910
import Home from './components/Router/Home.vue'
1011
import About from './components/Router/About.vue'
1112

12-
const routes = [
13-
{path: '/', component: Home},
14-
{path: '/about', component: About},
15-
]
13+
test('full app rendering/navigating from base URL', async () => {
14+
// Create a Router instance
15+
// https://next.router.vuejs.org/api/#createrouter
16+
// using a HTML5 history.
17+
// https://next.router.vuejs.org/api/#createwebhistory
18+
const router = createRouter({
19+
history: createWebHistory(),
20+
routes: [
21+
{path: '/', component: Home},
22+
{path: '/about', component: About},
23+
],
24+
})
1625

17-
test('full app rendering/navigating', async () => {
18-
// Notice how we pass a `routes` object to our render function.
19-
const {findByText, getByText, getByTestId} = render(App, {routes})
26+
const {findByText, getByText, getByTestId} = render(App, {
27+
global: {
28+
plugins: [router],
29+
},
30+
})
2031

2132
// Vue Router navigation is async, so we need to wait until the
2233
// initial render happens
2334
expect(await findByText('You are home')).toBeInTheDocument()
35+
expect(getByTestId('location-display')).toHaveTextContent('/')
2436

2537
await fireEvent.click(getByTestId('about-link'))
2638

@@ -32,3 +44,36 @@ test('full app rendering/navigating', async () => {
3244

3345
expect(getByText('You are on the about page')).toBeInTheDocument()
3446
})
47+
48+
test('sets router initial state', async () => {
49+
const Component = defineComponent({
50+
props: ['to'],
51+
template: `<router-link :to="to">Learn More</router-link>`,
52+
})
53+
54+
const route = {
55+
name: 'routeName',
56+
path: '/',
57+
component: defineComponent({template: `<div></div>`}),
58+
}
59+
60+
const router = createRouter({
61+
history: createWebHistory(),
62+
routes: [route],
63+
})
64+
65+
const to = {name: route.name}
66+
67+
const {getByText} = render(Component, {
68+
props: {to},
69+
global: {
70+
plugins: [router],
71+
},
72+
})
73+
74+
// We need to wait for router to complete the initial navigation.
75+
await router.isReady()
76+
77+
const button = getByText('Learn More')
78+
expect(button).toHaveAttribute('href', route.path)
79+
})

‎src/__tests__/vuex.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,46 @@
11
import '@testing-library/jest-dom'
2+
import {createStore} from 'vuex'
23
import {render, fireEvent} from '..'
34
import VuexTest from './components/Store/VuexTest'
45
import {store} from './components/Store/store'
56

7+
test('basic test with Vuex store', async () => {
8+
const storeInstance = createStore(store)
9+
10+
const {getByTestId, getByText} = render(VuexTest, {
11+
global: {
12+
plugins: [storeInstance],
13+
},
14+
})
15+
16+
expect(getByTestId('count-value')).toHaveTextContent('0')
17+
18+
await fireEvent.click(getByText('+'))
19+
expect(getByTestId('count-value')).toHaveTextContent('1')
20+
21+
await fireEvent.click(getByText('+'))
22+
expect(getByTestId('count-value')).toHaveTextContent('2')
23+
24+
await fireEvent.click(getByText('-'))
25+
expect(getByTestId('count-value')).toHaveTextContent('1')
26+
})
27+
628
// A common testing pattern is to create a custom renderer for a specific test
729
// file. This way, common operations such as registering a Vuex store can be
830
// abstracted out while avoiding sharing mutable state.
931
//
1032
// Tests should be completely isolated from one another.
1133
// Read this for additional context: https://kentcdodds.com/blog/test-isolation-with-react
1234
function renderVuexTestComponent(customStore) {
13-
// Render the component and merge the original store and the custom one
14-
// provided as a parameter. This way, we can alter some behaviors of the
15-
// initial implementation.
16-
return render(VuexTest, {store: {...store, ...customStore}})
35+
// Create a custom store with the original one and the one coming as a
36+
// parameter. This way we can alter some of its values.
37+
const mergedStoreInstance = createStore({...store, ...customStore})
38+
39+
return render(VuexTest, {
40+
global: {
41+
plugins: [mergedStoreInstance],
42+
},
43+
})
1744
}
1845

1946
test('can render with vuex with defaults', async () => {
@@ -26,7 +53,7 @@ test('can render with vuex with defaults', async () => {
2653

2754
test('can render with vuex with custom initial state', async () => {
2855
const {getByTestId, getByText} = renderVuexTestComponent({
29-
state: {count: 3},
56+
state: ()=>({count: 3}),
3057
})
3158

3259
await fireEvent.click(getByText('-'))
@@ -37,17 +64,21 @@ test('can render with vuex with custom initial state', async () => {
3764
test('can render with vuex with custom store', async () => {
3865
// This is a silly store that can never be changed.
3966
// eslint-disable-next-line no-shadow
40-
const store = {
41-
state: {count: 1000},
67+
const store = createStore({
68+
state: ()=>({count: 1000}),
4269
actions: {
4370
increment: () => jest.fn(),
4471
decrement: () => jest.fn(),
4572
},
46-
}
73+
})
4774

4875
// Notice how here we are not using the helper rendering method, because
4976
// there's no need to do that here. We're passing a whole store.
50-
const {getByTestId, getByText} = render(VuexTest, {store})
77+
const {getByTestId, getByText} = render(VuexTest, {
78+
global: {
79+
plugins: [store],
80+
},
81+
})
5182

5283
await fireEvent.click(getByText('+'))
5384
expect(getByTestId('count-value')).toHaveTextContent('1000')

‎src/render.js

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,15 @@ function render(
1919
const baseElement = customBaseElement || customContainer || document.body
2020
const container = customContainer || baseElement.appendChild(div)
2121

22-
const plugins = mountOptions.global?.plugins || []
23-
24-
if (store) {
25-
const {createStore} = require('vuex')
26-
plugins.push(createStore(store))
27-
}
28-
29-
if (routes) {
30-
const requiredRouter = require('vue-router')
31-
const {createRouter, createWebHistory} =
32-
requiredRouter.default || requiredRouter
33-
34-
const routerPlugin = createRouter({history: createWebHistory(), routes})
35-
plugins.push(routerPlugin)
22+
if (store || routes) {
23+
console.warn(`Providing 'store' or 'routes' options is no longer available.
24+
You need to create a router/vuex instance and provide it through 'global.plugins'.
25+
Check out the test examples on GitHub for further details.`)
3626
}
3727

3828
const wrapper = mount(Component, {
3929
...mountOptions,
4030
attachTo: container,
41-
global: {...mountOptions.global, plugins},
4231
})
4332

4433
// this removes the additional "data-v-app" div node from VTU:

‎types/index.d.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
import {EmitsOptions} from 'vue'
55
import {MountingOptions} from '@vue/test-utils'
6-
import {StoreOptions} from 'vuex'
7-
import {RouteRecordRaw} from 'vue-router'
86
import {queries, EventType, BoundFunctions} from '@testing-library/dom'
97
// eslint-disable-next-line import/no-extraneous-dependencies
108
import {OptionsReceived as PrettyFormatOptions} from 'pretty-format'
@@ -32,9 +30,15 @@ type VueTestUtilsRenderOptions = Omit<
3230
MountingOptions<Record<string, any>>,
3331
'attachTo' | 'shallow' | 'propsData'
3432
>
35-
type VueTestingLibraryRenderOptions = {
36-
store?: StoreOptions<{}>
37-
routes?: RouteRecordRaw[]
33+
interface VueTestingLibraryRenderOptions {
34+
/**
35+
* @deprecated Add a Vuex instance through `global.plugins` array instead.
36+
*/
37+
store?: any
38+
/**
39+
* @deprecated Add a Router instance through `global.plugins` array instead.
40+
*/
41+
routes?: any
3842
container?: Element
3943
baseElement?: Element
4044
}

‎types/test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,8 @@ export function testOptions() {
7777
},
7878
global: {
7979
config: {isCustomElement: _ => true},
80+
plugins: [],
8081
},
81-
store: {
82-
state: {count: 3},
83-
strict: true,
84-
},
85-
routes: [{path: '/', component: () => SomeComponent, name: 'route name'}],
8682
baseElement: document.createElement('div'),
8783
container: document.createElement('div'),
8884
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /