Skip to content

Commit

Permalink
feat: Warn using fireEvent.input() or .change() and suggest fireEvent…
Browse files Browse the repository at this point in the history
….update() (#166)

* Add warning message to fireEvent input and change when called directly #83

* Add user event test case

* Reword warning message to include used event key

* Fix template literal for warning messages
  • Loading branch information
jhack32 committed Nov 17, 2020
1 parent 04932ce commit 8871d2f
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 73 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@babel/plugin-transform-runtime": "^7.11.5",
"@testing-library/jest-dom": "^5.11.6",
"@types/estree": "0.0.45",
"@testing-library/user-event": "^12.1.10",
"apollo-boost": "^0.4.9",
"apollo-cache-inmemory": "^1.6.6",
"axios": "^0.20.0",
Expand Down
146 changes: 73 additions & 73 deletions src/__tests__/components/Form.vue
Original file line number Diff line number Diff line change
@@ -1,73 +1,73 @@
<template>
<div>
<h1>Movie Review</h1>
<form @submit.prevent="submit">
<label for="movie-input">Title of the movie</label>
<input id="movie-input" v-model="title" name="title" />

<label id="review-textarea">Your review</label>
<textarea
v-model="review"
name="review-textarea"
placeholder="Write an awesome review"
aria-labelledby="review-textarea"
/>

<label>
<input v-model="rating" type="radio" value="3" />
Wonderful
</label>
<label>
<input v-model="rating" type="radio" value="2" />
Average
</label>
<label>
<input v-model="rating" type="radio" value="1" />
Awful
</label>

<label id="recommend-label">Would you recommend this movie?</label>
<input
id="recommend"
v-model="recommend"
type="checkbox"
name="recommend"
/>

<button :disabled="submitDisabled" type="submit">
Submit
</button>
</form>
</div>
</template>

<script>
export default {
data() {
return {
title: '',
review: '',
rating: '1',
recommend: false,
}
},
computed: {
submitDisabled() {
return !this.title || !this.review
},
},
methods: {
submit() {
if (this.submitDisabled) return
this.$emit('submit', {
title: this.title,
review: this.review,
rating: this.rating,
recommend: this.recommend,
})
},
},
}
</script>
<template>
<div>
<h1>Movie Review</h1>
<form @submit.prevent="submit">
<label for="movie-input">Title of the movie</label>
<input id="movie-input" v-model="title" name="title" />

<label id="review-textarea">Your review</label>
<textarea
v-model="review"
name="review-textarea"
placeholder="Write an awesome review"
aria-labelledby="review-textarea"
/>

<label>
<input v-model="rating" type="radio" value="3" />
Wonderful
</label>
<label>
<input v-model="rating" type="radio" value="2" />
Average
</label>
<label>
<input v-model="rating" type="radio" value="1" />
Awful
</label>

<label id="recommend-label" for="recommend">
Would you recommend this movie?
</label>
<input
id="recommend"
v-model="recommend"
type="checkbox"
name="recommend"
/>

<button :disabled="submitDisabled" type="submit">Submit</button>
</form>
</div>
</template>

<script>
export default {
data() {
return {
title: '',
review: '',
rating: '1',
recommend: false,
}
},
computed: {
submitDisabled() {
return !this.title || !this.review
},
},
methods: {
submit() {
if (this.submitDisabled) return
this.$emit('submit', {
title: this.title,
review: this.review,
rating: this.rating,
recommend: this.recommend,
})
},
},
}
</script>
34 changes: 34 additions & 0 deletions src/__tests__/fire-event.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ const eventTypes = [
elementType: 'div',
},
]
beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {})
})

afterEach(() => {
console.warn.mockRestore()
})

// For each event type, we assert that the right events are being triggered
// when the associated fireEvent method is called.
Expand Down Expand Up @@ -181,6 +188,32 @@ test('calling `fireEvent` directly works too', async () => {

expect(emitted()).toHaveProperty('click')
})
const typingEvents = ['input', 'change']
typingEvents.forEach(event => {
test(`fireEvent.${event} prints a warning message to use fireEvent.update instead`, async () => {
const {getByTestId} = render({
template: `<input type="text" data-testid=test-${event}></input>`,
})

await fireEvent[event](getByTestId(`test-${event}`), 'hello')

expect(console.warn).toHaveBeenCalledTimes(1)
expect(console.warn).toHaveBeenCalledWith(
expect.stringContaining(
`Using "fireEvent.${event} may lead to unexpected results. Please use fireEvent.update() instead.`,
),
)
})
})
test('fireEvent.update does not trigger warning messages', async () => {
const {getByTestId} = render({
template: `<input type="text" data-testid=test-update></input>`,
})

await fireEvent.update(getByTestId('test-update'), 'hello')

expect(console.warn).not.toHaveBeenCalled()
})

test('fireEvent.update does not crash if non-input element is passed in', async () => {
const {getByText} = render({
Expand All @@ -194,4 +227,5 @@ test('fireEvent.update does not crash if non-input element is passed in', async
Hi
</div>
`)
expect(console.warn).not.toHaveBeenCalled()
})
2 changes: 2 additions & 0 deletions src/__tests__/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Form from './components/Form'
// Read 'What queries should I use?' for additional context:
// https://testing-library.com/docs/guide-which-query
test('Review form submits', async () => {
jest.spyOn(console, 'warn').mockImplementation(() => {})
const fakeReview = {
title: 'An Awesome Movie',
review: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
Expand Down Expand Up @@ -61,4 +62,5 @@ test('Review form submits', async () => {
// Assert the right event has been emitted.
expect(emitted()).toHaveProperty('submit')
expect(emitted().submit[0][0]).toMatchObject(fakeReview)
expect(console.warn).not.toHaveBeenCalled()
})
72 changes: 72 additions & 0 deletions src/__tests__/user-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import '@testing-library/jest-dom'
import {render} from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import Form from './components/Form'
import Select from './components/Select'

beforeEach(() => {
jest.spyOn(console, 'warn').mockImplementation(() => {})
})

afterEach(() => {
console.warn.mockRestore()
})

test('User events in a form', async () => {
const fakeReview = {
title: 'An Awesome Movie',
review: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
rating: '3',
}
const {getByText, getByLabelText, emitted} = render(Form)

const submitButton = getByText('Submit')
expect(submitButton).toBeDisabled()

const titleInput = getByLabelText(/title of the movie/i)
await userEvent.type(titleInput, fakeReview.title)
expect(titleInput.value).toEqual(fakeReview.title)

const textArea = getByLabelText(/Your review/i)
await userEvent.type(textArea, 'The t-rex went insane!')
expect(textArea.value).toEqual('The t-rex went insane!')

await userEvent.clear(textArea)
expect(textArea.value).toEqual('')
await userEvent.type(textArea, fakeReview.review)
expect(textArea.value).toEqual(fakeReview.review)

const initialSelectedRating = getByLabelText(/Awful/i)
const wonderfulRadioInput = getByLabelText(/Wonderful/i)
expect(initialSelectedRating).toBeChecked()
expect(wonderfulRadioInput).not.toBeChecked()

await userEvent.click(wonderfulRadioInput)
expect(wonderfulRadioInput).toBeChecked()
expect(initialSelectedRating).not.toBeChecked()

const recommendInput = getByLabelText(/Would you recommend this movie?/i)
await userEvent.click(recommendInput)
expect(recommendInput).toBeChecked()

userEvent.tab()
expect(submitButton).toHaveFocus()
expect(submitButton).toBeEnabled()
await userEvent.type(submitButton, '{enter}')
expect(emitted().submit[0][0]).toMatchObject(fakeReview)

expect(console.warn).not.toHaveBeenCalled()
})

test('selecting option with user events', async () => {
const {getByDisplayValue} = render(Select)
const select = getByDisplayValue('Tyrannosaurus')
expect(select.value).toBe('dino1')

await userEvent.selectOptions(select, 'dino2')
expect(select.value).toBe('dino2')

await userEvent.selectOptions(select, 'dino3')
expect(select.value).not.toBe('dino2')
expect(select.value).toBe('dino3')
})
7 changes: 7 additions & 0 deletions src/vue-testing-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,16 @@ async function fireEvent(...args) {
dtlFireEvent(...args)
await waitFor(() => {})
}
const changeOrInputEventCalledDirectly = (eventValue, eventKey) =>
eventValue && (eventKey === 'change' || eventKey === 'input')

Object.keys(dtlFireEvent).forEach(key => {
fireEvent[key] = async (...args) => {
if (changeOrInputEventCalledDirectly(args[1], key)) {
console.warn(
`Using "fireEvent.${key} may lead to unexpected results. Please use fireEvent.update() instead.`,
)
}
dtlFireEvent[key](...args)
await waitFor(() => {})
}
Expand Down

0 comments on commit 8871d2f

Please sign in to comment.