eslint-plugin-listeners
Version:
ESLint plugin for preventing memory leaks around event listeners
269 lines (238 loc) • 6.16 kB
text/typescript
import mocha from 'mocha';
import { RuleTester } from '@typescript-eslint/rule-tester';
import { createRule } from '../../../src/rules/event-listener';
import { RuleType } from '../../../src/utils';
RuleTester.afterAll = mocha.after;
const ruleTester = new RuleTester({
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
parser: '@typescript-eslint/parser',
});
ruleTester.run('matching-remove-event-listener', createRule(RuleType.MatchingRemoveEventListener), {
valid: [
{
code: `
const handleClack = () => {
console.log('click clack')
}
class App {
handleRootNodeClick = () => {
console.log('click')
}
componentDidMount() {
this.rootNodeRef.addEventListener('click', this.handleRootNodeClick)
this.rootNodeRef.addEventListener('clack', handleClack)
}
componentWillUnmount() {
this.rootNodeRef.removeEventListener('click', this.handleRootNodeClick)
this.rootNodeRef.removeEventListener('clack', handleClack)
}
render() {
return (
<div ref={node => (this.rootNodeRef = node)} />
)
}
}
`,
},
{
code: `
const clickHandler = () => {
console.log('click')
}
const anotherClickHandler = () => {
console.log('click')
}
class App {
componentDidMount() {
this.rootNodeRef.addEventListener('click', clickHandler)
this.rootNodeRef.addEventListener('click', anotherClickHandler)
}
componentWillUnmount() {
this.rootNodeRef.removeEventListener('click', anotherClickHandler)
}
render() {
return (
<div ref={node => (this.rootNodeRef = node)} />
)
}
}
`,
},
{
code: `
const emitter = new EventEmitter()
const dataHandler = () => {
console.log('data')
}
const data2Handler = () => {
console.log('data')
}
emitter.on('data', dataHandler)
emitter.once('close', () => {
console.log('close')
emitter.removeListener('data', dataHandler)
})
`,
},
],
invalid: [
{
code: `
class App {
handleRootNodeClick = () => {
console.log('click')
}
handleRootNodeKeyPress = () => {
console.log('keyPress')
}
componentDidMount() {
window.addEventListener('click', this.handleRootNodeClick)
}
componentWillUnmount() {
window.removeEventListener('click', this.handleRootNodeKeyPress)
}
render() {
return null
}
}
`,
errors: [
{
messageId: 'listenersDoNotMatch',
data: {
add: 'this.handleRootNodeClick',
remove: 'this.handleRootNodeKeyPress',
element: 'window',
eventName: 'click',
},
},
],
},
{
code: `
const clickHandler = () => {
console.log('click')
}
const anotherClickHandler = () => {
console.log('click')
}
class App {
componentDidMount() {
this.rootNodeRef.addEventListener('click', clickHandler)
}
componentWillUnmount() {
this.rootNodeRef.removeEventListener('click', anotherClickHandler)
}
render() {
return (
<div ref={node => (this.rootNodeRef = node)} />
)
}
}
`,
errors: [
{
messageId: 'listenersDoNotMatch',
data: {
add: 'clickHandler',
remove: 'anotherClickHandler',
element: 'this.rootNodeRef',
eventName: 'click',
},
},
],
},
{
code: `
const clickHandler = () => {
console.log('click')
}
class App {
componentDidMount() {
this.rootNodeRef.addEventListener('click', clickHandler, true)
}
componentWillUnmount() {
this.rootNodeRef.removeEventListener('click', clickHandler)
}
render() {
return (
<div ref={node => (this.rootNodeRef = node)} />
)
}
}
`,
errors: [
{
messageId: 'listenersDoNotMatchUseCapture',
data: {
remove: 'clickHandler',
element: 'this.rootNodeRef',
eventName: 'click',
},
},
],
},
{
code: `
const clickHandler = () => {
console.log('click')
}
class App {
componentDidMount() {
const useCapture = true;
this.rootNodeRef.addEventListener('click', clickHandler, useCapture)
}
componentWillUnmount() {
this.rootNodeRef.removeEventListener('click', clickHandler)
}
render() {
return (
<div ref={node => (this.rootNodeRef = node)} />
)
}
}
`,
errors: [
{
messageId: 'listenersDoNotMatchUseCapture',
data: {
remove: 'clickHandler',
element: 'this.rootNodeRef',
eventName: 'click',
},
},
],
},
{
code: `
const emitter = new EventEmitter()
const dataHandler = () => {
console.log('data')
}
const data2Handler = () => {
console.log('data')
}
emitter.on('data', dataHandler)
emitter.once('close', () => {
console.log('close')
emitter.removeListener('data', dataHandler2)
})
`,
errors: [
{
messageId: 'listenersDoNotMatch',
data: {
add: 'dataHandler',
remove: 'dataHandler2',
element: 'emitter',
eventName: 'data',
},
},
],
},
],
});