diff --git a/lib/rules/no-container.ts b/lib/rules/no-container.ts index 54423db5..29960220 100644 --- a/lib/rules/no-container.ts +++ b/lib/rules/no-container.ts @@ -86,6 +86,10 @@ export default createTestingLibraryRule({ } } + function isDisallowedContainerProperty(propertyName: string): boolean { + return propertyName === 'innerHTML'; + } + return { CallExpression(node) { const callExpressionIdentifier = getDeepestIdentifierNode(node); @@ -111,6 +115,20 @@ export default createTestingLibraryRule({ } }, + MemberExpression(node) { + if ( + ASTUtils.isIdentifier(node.object) && + node.object.name === containerName && + ASTUtils.isIdentifier(node.property) && + isDisallowedContainerProperty(node.property.name) + ) { + context.report({ + node, + messageId: 'noContainer', + }); + } + }, + VariableDeclarator(node) { if (!node.init) { return; @@ -150,12 +168,18 @@ export default createTestingLibraryRule({ if (ASTUtils.isIdentifier(nodeValue)) { containerName = nodeValue.name; } else if (isObjectPattern(nodeValue)) { - nodeValue.properties.forEach( - (property) => - isProperty(property) && - ASTUtils.isIdentifier(property.key) && - destructuredContainerPropNames.push(property.key.name) - ); + nodeValue.properties.forEach((property) => { + if (isProperty(property) && ASTUtils.isIdentifier(property.key)) { + if (isDisallowedContainerProperty(property.key.name)) { + context.report({ + node, + messageId: 'noContainer', + }); + } + + destructuredContainerPropNames.push(property.key.name); + } + }); } } else if (ASTUtils.isIdentifier(node.id)) { renderResultVarName = node.id.name; diff --git a/tests/lib/rules/no-container.test.ts b/tests/lib/rules/no-container.test.ts index 6967de9e..15994493 100644 --- a/tests/lib/rules/no-container.test.ts +++ b/tests/lib/rules/no-container.test.ts @@ -74,9 +74,46 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { otherRender } from 'somewhere-else' const { container } = otherRender(); + container.innerHTML; const button = container.querySelector('.btn-primary'); `, }, + { + code: ` + const { container } = render(); + const newElement = document.createElement('div'); + newElement.innerHTML; + `, + }, + { + code: ` + const { container } = render(); + const newElement = document.createElement('div'); + newElement.firstChild; + `, + }, + { + code: ` + const { container } = render(); + container.firstChild; + `, + }, + { + code: ` + const { container: { firstChild } } = render(); + `, + }, + ...SUPPORTED_TESTING_FRAMEWORKS.map( + (testingFramework) => + ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render as renamed } from '${testingFramework}' + import { render } from 'somewhere-else' + const { container: { innerHTML } } = render(); + `, + } as const) + ), ], invalid: [ { @@ -107,6 +144,20 @@ ruleTester.run(RULE_NAME, rule, { }, ], }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from 'test-utils' + const { container: { innerHTML } } = render(); + `, + errors: [ + { + line: 3, + column: 15, + messageId: 'noContainer', + }, + ], + }, ...SUPPORTED_TESTING_FRAMEWORKS.map( (testingFramework) => ({ @@ -146,6 +197,45 @@ ruleTester.run(RULE_NAME, rule, { ], } as const) ), + ...SUPPORTED_TESTING_FRAMEWORKS.map( + (testingFramework) => + ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render as testingRender } from '${testingFramework}' + const { container: renamed } = testingRender(); + renamed.innerHTML; + `, + errors: [ + { + line: 4, + column: 9, + messageId: 'noContainer', + }, + ], + } as const) + ), + ...SUPPORTED_TESTING_FRAMEWORKS.map( + (testingFramework) => + ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from '${testingFramework}' + + const setup = () => render() + + const { container } = setup() + container.innerHTML; + `, + errors: [ + { + line: 7, + column: 9, + messageId: 'noContainer', + }, + ], + } as const) + ), { code: ` const { container } = render(); @@ -232,5 +322,101 @@ ruleTester.run(RULE_NAME, rule, { }, ], }, + { + code: ` + const { container } = render(); + container.innerHTML; + `, + errors: [ + { + line: 3, + column: 5, + messageId: 'noContainer', + }, + ], + }, + { + code: ` + const { container: alias } = render(); + alias.innerHTML; + `, + errors: [ + { + line: 3, + column: 5, + messageId: 'noContainer', + }, + ], + }, + { + code: ` + const { container: { innerHTML } } = render(); + `, + errors: [ + { + line: 2, + column: 11, + messageId: 'noContainer', + }, + ], + }, + { + code: ` + const { container: { innerHTML: alias } } = render(); + `, + errors: [ + { + line: 2, + column: 11, + messageId: 'noContainer', + }, + ], + }, + ...SUPPORTED_TESTING_FRAMEWORKS.map( + (testingFramework) => + ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from '${testingFramework}' + const { container: { innerHTML } } = render(); + `, + errors: [ + { + line: 3, + column: 15, + messageId: 'noContainer', + }, + ], + } as const) + ), + { + settings: { + 'testing-library/custom-renders': ['customRender', 'renderWithRedux'], + }, + code: ` + const { container } = renderWithRedux(); + container.innerHTML; + `, + errors: [ + { + line: 3, + column: 9, + messageId: 'noContainer', + }, + ], + }, + // { + // code: ` + // const view = render(); + // view.container.innerHTML; + // `, + // errors: [ + // { + // line: 3, + // column: 29, + // messageId: 'noContainer', + // }, + // ], + // }, ], });