Skip to content

fix: Aggressive Reporting support in resolveToTestingLibraryFn #1043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions lib/create-testing-library-rule/detect-testing-library-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ import {
ASYNC_UTILS,
DEBUG_UTILS,
PRESENCE_MATCHERS,
USER_EVENT_MODULE,
} from '../utils';
import {
isCustomTestingLibraryModule,
isOfficialTestingLibraryModule,
isTestingLibraryModule,
} from '../utils/is-testing-library-module';

const SETTING_OPTION_OFF = 'off';

Expand Down Expand Up @@ -133,7 +139,6 @@ export interface DetectionHelpers {
isNodeComingFromTestingLibrary: IsNodeComingFromTestingLibraryFn;
}

const USER_EVENT_PACKAGE = '@testing-library/user-event';
const REACT_DOM_TEST_UTILS_PACKAGE = 'react-dom/test-utils';
const FIRE_EVENT_NAME = 'fireEvent';
const CREATE_EVENT_NAME = 'createEvent';
Expand Down Expand Up @@ -960,12 +965,11 @@ export function detectTestingLibraryUtils<
}

const hasImportElementMatch = hasImportMatch(importNode, identifierName);
const hasImportModuleMatch =
/testing-library/g.test(importDeclarationName) ||
(typeof customModuleSetting === 'string' &&
importDeclarationName.endsWith(customModuleSetting));

return hasImportElementMatch && hasImportModuleMatch;
return (
hasImportElementMatch &&
isTestingLibraryModule(importDeclarationName, customModuleSetting)
);
};

const helpers: DetectionHelpers = {
Expand Down Expand Up @@ -1017,17 +1021,16 @@ export function detectTestingLibraryUtils<
}
// check only if testing library import not found yet so we avoid
// to override importedTestingLibraryNodes after it's found
if (/testing-library/g.test(node.source.value)) {
if (isOfficialTestingLibraryModule(node.source.value)) {
importedTestingLibraryNodes.push(node);
}

// check only if custom module import not found yet so we avoid
// to override importedCustomModuleNode after it's found
const customModule = getCustomModule();
if (
customModule &&
!importedCustomModuleNode &&
node.source.value.endsWith(customModule)
isCustomTestingLibraryModule(node.source.value, customModule)
) {
importedCustomModuleNode = node;
}
Expand All @@ -1036,7 +1039,7 @@ export function detectTestingLibraryUtils<
// to override importedUserEventLibraryNode after it's found
if (
!importedUserEventLibraryNode &&
node.source.value === USER_EVENT_PACKAGE
node.source.value === USER_EVENT_MODULE
) {
importedUserEventLibraryNode = node;
}
Expand All @@ -1063,7 +1066,7 @@ export function detectTestingLibraryUtils<
(arg) =>
isLiteral(arg) &&
typeof arg.value === 'string' &&
/testing-library/g.test(arg.value)
isOfficialTestingLibraryModule(arg.value)
)
) {
importedTestingLibraryNodes.push(callExpression);
Expand All @@ -1074,10 +1077,9 @@ export function detectTestingLibraryUtils<
!importedCustomModuleNode &&
args.some(
(arg) =>
customModule &&
isLiteral(arg) &&
typeof arg.value === 'string' &&
arg.value.endsWith(customModule)
isCustomTestingLibraryModule(arg.value, customModule)
)
) {
importedCustomModuleNode = callExpression;
Expand All @@ -1089,7 +1091,7 @@ export function detectTestingLibraryUtils<
(arg) =>
isLiteral(arg) &&
typeof arg.value === 'string' &&
arg.value === USER_EVENT_PACKAGE
arg.value === USER_EVENT_MODULE
)
) {
importedUserEventLibraryNode = callExpression;
Expand Down
7 changes: 7 additions & 0 deletions lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ const LIBRARY_MODULES = [

const USER_EVENT_MODULE = '@testing-library/user-event';

const OLD_LIBRARY_MODULES = [
'dom-testing-library',
'vue-testing-library',
'react-testing-library',
] as const;

const SYNC_QUERIES_VARIANTS = [
'getBy',
'getAllBy',
Expand Down Expand Up @@ -154,4 +160,5 @@ export {
ABSENCE_MATCHERS,
EVENT_HANDLER_METHODS,
USER_EVENT_MODULE,
OLD_LIBRARY_MODULES,
};
22 changes: 22 additions & 0 deletions lib/utils/is-testing-library-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { TestingLibrarySettings } from '../create-testing-library-rule/detect-testing-library-utils';

import { LIBRARY_MODULES, OLD_LIBRARY_MODULES, USER_EVENT_MODULE } from '.';

export const isOfficialTestingLibraryModule = (importSourceName: string) =>
[...OLD_LIBRARY_MODULES, ...LIBRARY_MODULES, USER_EVENT_MODULE].includes(
importSourceName
);

export const isCustomTestingLibraryModule = (
importSourceName: string,
customModuleSetting: TestingLibrarySettings['testing-library/utils-module']
) =>
typeof customModuleSetting === 'string' &&
importSourceName.endsWith(customModuleSetting);

export const isTestingLibraryModule = (
importSourceName: string,
customModuleSetting?: TestingLibrarySettings['testing-library/utils-module']
) =>
isOfficialTestingLibraryModule(importSourceName) ||
isCustomTestingLibraryModule(importSourceName, customModuleSetting);
9 changes: 3 additions & 6 deletions lib/utils/resolve-to-testing-library-fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
isSupportedAccessor,
} from '../node-utils/accessors';

import { LIBRARY_MODULES, USER_EVENT_MODULE } from '.';
import { isTestingLibraryModule } from './is-testing-library-module';

interface ImportDetails {
source: string;
Expand Down Expand Up @@ -171,11 +171,8 @@ export const resolveToTestingLibraryFn = <
}

const customModuleSetting = context.settings['testing-library/utils-module'];
if (
[...LIBRARY_MODULES, USER_EVENT_MODULE, customModuleSetting].some(
(module) => module === maybeImport.source
)
) {

if (isTestingLibraryModule(maybeImport.source, customModuleSetting)) {
return {
original: maybeImport.imported,
local: maybeImport.local,
Expand Down
10 changes: 10 additions & 0 deletions tests/lib/rules/no-node-access.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ ruleTester.run(RULE_NAME, rule, {

const buttonText = screen.getByText('submit');
fe.click(buttonText);
`,
},
{
settings: { 'testing-library/utils-module': 'test-utils' },
code: `
// case: custom module set but not imported using ${testingFramework} (aggressive reporting limited)
import { screen, fireEvent } from '../test-utils';

const buttonText = screen.getByText('submit');
fireEvent.click(buttonText);
`,
},
{
Expand Down
83 changes: 83 additions & 0 deletions tests/lib/utils/is-testing-library-module.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
LIBRARY_MODULES,
OLD_LIBRARY_MODULES,
USER_EVENT_MODULE,
} from '../../../lib/utils';
import {
isCustomTestingLibraryModule,
isOfficialTestingLibraryModule,
isTestingLibraryModule,
} from '../../../lib/utils/is-testing-library-module';

describe('isOfficialTestingLibraryModule', () => {
it.each([...OLD_LIBRARY_MODULES, ...LIBRARY_MODULES, USER_EVENT_MODULE])(
'returns true when arg is "%s"',
(importSourceName) => {
const result = isOfficialTestingLibraryModule(importSourceName);

expect(result).toBe(true);
}
);

it.each(['custom-modules', 'hoge-testing-library', '@testing-library/hoge'])(
'returns false when arg is "%s"',
(importSourceName) => {
const result = isOfficialTestingLibraryModule(importSourceName);

expect(result).toBe(false);
}
);
});

describe('isCustomTestingLibraryModule', () => {
it.each(['test-utils', '../test-utils', '@/test-utils'])(
'returns true when arg is "%s"',
(importSourceName) => {
const result = isCustomTestingLibraryModule(
importSourceName,
'test-utils'
);

expect(result).toBe(true);
}
);

it.each([
'custom-modules',
'react-testing-library',
'@testing-library/react',
'test-util',
'test-utils-module',
])('returns false when arg is "%s"', (importSourceName) => {
const result = isCustomTestingLibraryModule(importSourceName, 'test-utils');

expect(result).toBe(false);
});
});

describe('isTestingLibraryModule', () => {
it.each([
...OLD_LIBRARY_MODULES,
...LIBRARY_MODULES,
USER_EVENT_MODULE,
'test-utils',
'../test-utils',
'@/test-utils',
])('returns true when arg is "%s"', (importSourceName) => {
const result = isTestingLibraryModule(importSourceName, 'test-utils');

expect(result).toBe(true);
});

it.each([
'custom-modules',
'hoge-testing-library',
'@testing-library/hoge',
'test-util',
'test-utils-module',
])('returns false when arg is "%s"', (importSourceName) => {
const result = isTestingLibraryModule(importSourceName, 'test-utils');

expect(result).toBe(false);
});
});
47 changes: 38 additions & 9 deletions tests/lib/utils/resolve-to-testing-library-fn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,6 @@ ruleTester.run('esm', rule, {
userEvent.default.setup()
`,
},
{
// Verifies that a local './test-utils' import doesn't match the configured 'test-utils' utils module
settings: { 'testing-library/utils-module': 'test-utils' },
code: `
import { userEvent } from './test-utils';

userEvent.setup()
`,
},
...LIBRARY_MODULES.map((module) => ({
code: `
import * as testingLibrary from '${module}';
Expand Down Expand Up @@ -172,6 +163,44 @@ ruleTester.run('esm', rule, {
},
],
},
{
settings: { 'testing-library/utils-module': 'test-utils' },
code: `
import userEvent from '../test-utils';

userEvent.setup()
`,
errors: [
{
messageId: 'details',
data: {
data: {
original: null,
local: 'userEvent',
},
},
},
],
},
{
settings: { 'testing-library/utils-module': 'test-utils' },
code: `
import userEvent from '@/test-utils';

userEvent.setup()
`,
errors: [
{
messageId: 'details',
data: {
data: {
original: null,
local: 'userEvent',
},
},
},
],
},
{
settings: {
'testing-library/custom-renders': ['customRender', 'renderWithRedux'],
Expand Down