Skip to content

Commit fd81fc5

Browse files
authored
fix(prefer-user-event): format list of userEvent methods correctly (#311)
* fix(prefer-user-event): format list of userEvent methods correctly * test(prefer-user-event): check error data in all invalid cases Closes #310
1 parent eaca948 commit fd81fc5

File tree

2 files changed

+140
-28
lines changed

2 files changed

+140
-28
lines changed

lib/rules/prefer-user-event.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const UserEventMethods = [
2323
type UserEventMethodsType = typeof UserEventMethods[number];
2424

2525
// maps fireEvent methods to userEvent. Those not found here, do not have an equivalent (yet)
26-
export const MappingToUserEvent: Record<string, UserEventMethodsType[]> = {
26+
export const MAPPING_TO_USER_EVENT: Record<string, UserEventMethodsType[]> = {
2727
click: ['click', 'type', 'selectOptions', 'deselectOptions'],
2828
change: ['upload', 'type', 'clear', 'selectOptions', 'deselectOptions'],
2929
dblClick: ['dblClick'],
@@ -49,17 +49,15 @@ export const MappingToUserEvent: Record<string, UserEventMethodsType[]> = {
4949
};
5050

5151
function buildErrorMessage(fireEventMethod: string) {
52-
const allMethods = MappingToUserEvent[fireEventMethod].map(
53-
(method: string) => `userEvent.${method}()`
52+
const userEventMethods = MAPPING_TO_USER_EVENT[fireEventMethod].map(
53+
(methodName) => `userEvent.${methodName}`
5454
);
55-
const { length } = allMethods;
5655

57-
const init = length > 2 ? allMethods.slice(0, length - 2).join(', ') : '';
58-
const last = `${length > 1 ? ' or ' : ''}${allMethods[length - 1]}`;
59-
return `${init}${last}`;
56+
// TODO: when min node version is 13, we can reimplement this using `Intl.ListFormat`
57+
return userEventMethods.join(', ').replace(/, ([a-zA-Z.]+)$/, ', or $1');
6058
}
6159

62-
const fireEventMappedMethods = Object.keys(MappingToUserEvent);
60+
const fireEventMappedMethods = Object.keys(MAPPING_TO_USER_EVENT);
6361

6462
export default createTestingLibraryRule<Options, MessageIds>({
6563
name: RULE_NAME,
@@ -72,7 +70,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
7270
},
7371
messages: {
7472
preferUserEvent:
75-
'Prefer using {{userEventMethods}} over fireEvent.{{fireEventMethod}}()',
73+
'Prefer using {{userEventMethods}} over fireEvent.{{fireEventMethod}}',
7674
},
7775
schema: [
7876
{

tests/lib/rules/prefer-user-event.test.ts

+133-19
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import {
55
import { createRuleTester } from '../test-utils';
66
import { LIBRARY_MODULES } from '../../../lib/utils';
77
import rule, {
8-
RULE_NAME,
8+
MAPPING_TO_USER_EVENT,
99
MessageIds,
1010
Options,
11+
RULE_NAME,
1112
UserEventMethods,
12-
MappingToUserEvent,
1313
} from '../../../lib/rules/prefer-user-event';
1414

1515
function createScenarioWithImport<
@@ -18,7 +18,7 @@ function createScenarioWithImport<
1818
return LIBRARY_MODULES.reduce(
1919
(acc: Array<T>, libraryModule) =>
2020
acc.concat(
21-
Object.keys(MappingToUserEvent).map((fireEventMethod) =>
21+
Object.keys(MAPPING_TO_USER_EVENT).map((fireEventMethod) =>
2222
callback(libraryModule, fireEventMethod)
2323
)
2424
),
@@ -28,6 +28,26 @@ function createScenarioWithImport<
2828

2929
const ruleTester = createRuleTester();
3030

31+
function formatUserEventMethodsMessage(fireEventMethod: string): string {
32+
const userEventMethods = MAPPING_TO_USER_EVENT[fireEventMethod].map(
33+
(methodName) => `userEvent.${methodName}`
34+
);
35+
let joinedList = '';
36+
37+
for (let i = 0; i < userEventMethods.length; i++) {
38+
const item = userEventMethods[i];
39+
if (i === 0) {
40+
joinedList += item;
41+
} else if (i + 1 === userEventMethods.length) {
42+
joinedList += `, or ${item}`;
43+
} else {
44+
joinedList += `, ${item}`;
45+
}
46+
}
47+
48+
return joinedList;
49+
}
50+
3151
ruleTester.run(RULE_NAME, rule, {
3252
valid: [
3353
{
@@ -132,7 +152,7 @@ ruleTester.run(RULE_NAME, rule, {
132152
userEvent.${userEventMethod}(foo)
133153
`,
134154
})),
135-
...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({
155+
...Object.keys(MAPPING_TO_USER_EVENT).map((fireEventMethod: string) => ({
136156
settings: {
137157
'testing-library/utils-module': 'test-utils',
138158
},
@@ -143,7 +163,7 @@ ruleTester.run(RULE_NAME, rule, {
143163
fireEvent.${fireEventMethod}(foo)
144164
`,
145165
})),
146-
...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({
166+
...Object.keys(MAPPING_TO_USER_EVENT).map((fireEventMethod: string) => ({
147167
settings: {
148168
'testing-library/utils-module': 'test-utils',
149169
},
@@ -154,7 +174,7 @@ ruleTester.run(RULE_NAME, rule, {
154174
`,
155175
options: [{ allowedMethods: [fireEventMethod] }],
156176
})),
157-
...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({
177+
...Object.keys(MAPPING_TO_USER_EVENT).map((fireEventMethod: string) => ({
158178
settings: {
159179
'testing-library/utils-module': 'test-utils',
160180
},
@@ -165,7 +185,7 @@ ruleTester.run(RULE_NAME, rule, {
165185
`,
166186
options: [{ allowedMethods: [fireEventMethod] }],
167187
})),
168-
...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({
188+
...Object.keys(MAPPING_TO_USER_EVENT).map((fireEventMethod: string) => ({
169189
settings: {
170190
'testing-library/utils-module': 'test-utils',
171191
},
@@ -198,6 +218,10 @@ ruleTester.run(RULE_NAME, rule, {
198218
messageId: 'preferUserEvent',
199219
line: 4,
200220
column: 9,
221+
data: {
222+
userEventMethods: formatUserEventMethodsMessage(fireEventMethod),
223+
fireEventMethod: fireEventMethod,
224+
},
201225
},
202226
],
203227
})
@@ -208,7 +232,17 @@ ruleTester.run(RULE_NAME, rule, {
208232
import * as dom from '${libraryModule}'
209233
dom.fireEvent.${fireEventMethod}(foo)
210234
`,
211-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
235+
errors: [
236+
{
237+
messageId: 'preferUserEvent',
238+
line: 3,
239+
column: 9,
240+
data: {
241+
userEventMethods: formatUserEventMethodsMessage(fireEventMethod),
242+
fireEventMethod: fireEventMethod,
243+
},
244+
},
245+
],
212246
})
213247
),
214248
...createScenarioWithImport<InvalidTestCase<MessageIds, Options>>(
@@ -217,7 +251,17 @@ ruleTester.run(RULE_NAME, rule, {
217251
const { fireEvent } = require('${libraryModule}')
218252
fireEvent.${fireEventMethod}(foo)
219253
`,
220-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
254+
errors: [
255+
{
256+
messageId: 'preferUserEvent',
257+
line: 3,
258+
column: 9,
259+
data: {
260+
userEventMethods: formatUserEventMethodsMessage(fireEventMethod),
261+
fireEventMethod: fireEventMethod,
262+
},
263+
},
264+
],
221265
})
222266
),
223267
...createScenarioWithImport<InvalidTestCase<MessageIds, Options>>(
@@ -226,10 +270,20 @@ ruleTester.run(RULE_NAME, rule, {
226270
const rtl = require('${libraryModule}')
227271
rtl.fireEvent.${fireEventMethod}(foo)
228272
`,
229-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
273+
errors: [
274+
{
275+
messageId: 'preferUserEvent',
276+
line: 3,
277+
column: 9,
278+
data: {
279+
userEventMethods: formatUserEventMethodsMessage(fireEventMethod),
280+
fireEventMethod: fireEventMethod,
281+
},
282+
},
283+
],
230284
})
231285
),
232-
...Object.keys(MappingToUserEvent).map(
286+
...Object.keys(MAPPING_TO_USER_EVENT).map(
233287
(fireEventMethod: string) =>
234288
({
235289
settings: {
@@ -239,10 +293,22 @@ ruleTester.run(RULE_NAME, rule, {
239293
import * as dom from 'test-utils'
240294
dom.fireEvent.${fireEventMethod}(foo)
241295
`,
242-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
296+
errors: [
297+
{
298+
messageId: 'preferUserEvent',
299+
line: 3,
300+
column: 9,
301+
data: {
302+
userEventMethods: formatUserEventMethodsMessage(
303+
fireEventMethod
304+
),
305+
fireEventMethod: fireEventMethod,
306+
},
307+
},
308+
],
243309
} as const)
244310
),
245-
...Object.keys(MappingToUserEvent).map(
311+
...Object.keys(MAPPING_TO_USER_EVENT).map(
246312
(fireEventMethod: string) =>
247313
({
248314
settings: {
@@ -252,10 +318,22 @@ ruleTester.run(RULE_NAME, rule, {
252318
import { fireEvent } from 'test-utils'
253319
fireEvent.${fireEventMethod}(foo)
254320
`,
255-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
321+
errors: [
322+
{
323+
messageId: 'preferUserEvent',
324+
line: 3,
325+
column: 9,
326+
data: {
327+
userEventMethods: formatUserEventMethodsMessage(
328+
fireEventMethod
329+
),
330+
fireEventMethod: fireEventMethod,
331+
},
332+
},
333+
],
256334
} as const)
257335
),
258-
...Object.keys(MappingToUserEvent).map(
336+
...Object.keys(MAPPING_TO_USER_EVENT).map(
259337
(fireEventMethod: string) =>
260338
({
261339
code: `
@@ -264,10 +342,22 @@ ruleTester.run(RULE_NAME, rule, {
264342
import { fireEvent } from 'test-utils'
265343
fireEvent.${fireEventMethod}(foo)
266344
`,
267-
errors: [{ messageId: 'preferUserEvent', line: 5, column: 9 }],
345+
errors: [
346+
{
347+
messageId: 'preferUserEvent',
348+
line: 5,
349+
column: 9,
350+
data: {
351+
userEventMethods: formatUserEventMethodsMessage(
352+
fireEventMethod
353+
),
354+
fireEventMethod: fireEventMethod,
355+
},
356+
},
357+
],
268358
} as const)
269359
),
270-
...Object.keys(MappingToUserEvent).map(
360+
...Object.keys(MAPPING_TO_USER_EVENT).map(
271361
(fireEventMethod: string) =>
272362
({
273363
settings: {
@@ -277,14 +367,27 @@ ruleTester.run(RULE_NAME, rule, {
277367
import { fireEvent as fireEventAliased } from 'test-utils'
278368
fireEventAliased.${fireEventMethod}(foo)
279369
`,
280-
errors: [{ messageId: 'preferUserEvent', line: 3, column: 9 }],
370+
errors: [
371+
{
372+
messageId: 'preferUserEvent',
373+
line: 3,
374+
column: 9,
375+
data: {
376+
userEventMethods: formatUserEventMethodsMessage(
377+
fireEventMethod
378+
),
379+
fireEventMethod: fireEventMethod,
380+
},
381+
},
382+
],
281383
} as const)
282384
),
283385
{
284386
code: ` // simple test to check error in detail
285387
import { fireEvent } from '@testing-library/react'
286388
287389
fireEvent.click(element)
390+
fireEvent.mouseOut(element)
288391
`,
289392
errors: [
290393
{
@@ -295,10 +398,21 @@ ruleTester.run(RULE_NAME, rule, {
295398
endColumn: 22,
296399
data: {
297400
userEventMethods:
298-
'userEvent.click(), userEvent.type() or userEvent.deselectOptions()',
401+
'userEvent.click, userEvent.type, userEvent.selectOptions, or userEvent.deselectOptions',
299402
fireEventMethod: 'click',
300403
},
301404
},
405+
{
406+
messageId: 'preferUserEvent',
407+
line: 5,
408+
endLine: 5,
409+
column: 7,
410+
endColumn: 25,
411+
data: {
412+
userEventMethods: 'userEvent.unhover',
413+
fireEventMethod: 'mouseOut',
414+
},
415+
},
302416
],
303417
},
304418
],

0 commit comments

Comments
 (0)