Skip to content

Commit

Permalink
hopefully resolve syntax issues with combinatorial testing
Browse files Browse the repository at this point in the history
  • Loading branch information
vwillyams committed Oct 23, 2024
1 parent cf6a63f commit cb7e496
Showing 1 changed file with 65 additions and 14 deletions.
79 changes: 65 additions & 14 deletions tests/lib/rules/no-multi-comp.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,23 @@ const parserOptions = {
/**
* @type {ComponentGenerator[]} Array of different ways to output valid code for instantiating a React component with the given name.
*/
const COMPONENT_TYPES = [
const SIMPLE_COMPONENT_TYPES = [
(name) => `const ${name} = () => <></>;`, // arrow function component
(name) => `let ${name} = () => <></>;`, // arrow function component (with let)
(name) => `var ${name} = () => <></>;`, // arrow function component (with var)
(name) => `const ${name} = memo(() => <></>);`, // memoized anonymous component
(name) => `const ${name} = async () => <></>;`, // async component (react server components)
];

// eslint-disable-next-line valid-jsdoc
/**
* @type {ComponentGenerator[]} Array of different ways to output valid code for instantiating a React component with the given name.
*/
const COMPLEX_COMPONENT_TYPES = [
(name) => `function ${name}() { return <></>; }`, // standard function component
(name) => `class ${name} extends React.Component {
render() { return <></>; }
}`, // class component
(name) => `const ${name} = memo(() => <></>);`, // memoized anonymous component
(name) => `const ${name} = async () => <></>;`, // async component (react server components)
];

/**
Expand All @@ -72,9 +79,6 @@ const EXPORT_TYPES_VALID = [
(compOne, compOneName, compTwo, compTwoName) => `
export ${compOne(compOneName)}
${compTwo(compTwoName)}`, // standard export
(compOne, compOneName, compTwo, compTwoName) => `
export default ${compOne(compOneName)}
${compTwo(compTwoName)}`, // default export
(compOne, compOneName, compTwo, compTwoName, exportRename) => `
${compOne(compOneName)}
${compTwo(compTwoName)}
Expand Down Expand Up @@ -103,6 +107,18 @@ const EXPORT_TYPES_VALID = [
export default function() { return <${compOneName} />; }`, // exporting component with indirection
];

// eslint-disable-next-line valid-jsdoc
/**
* Special case: inline `export default` syntax cannot be followed by `const, let, var`
*
* @type {ComponentExportGenerator[]}
*/
const EXPORT_TYPES_VALID_COMPLEX = [
(compOne, compOneName, compTwo, compTwoName) => `
export default ${compOne(compOneName)}
${compTwo(compTwoName)}`, // default export
];

// eslint-disable-next-line valid-jsdoc
/**
* @type {ComponentExportGenerator[]}
Expand All @@ -112,9 +128,6 @@ const EXPORT_TYPES_INVALID = [
(compOne, compOneName, compTwo, compTwoName) => `
export ${compOne(compOneName)}
export ${compTwo(compTwoName)}`, // standard export
(compOne, compOneName, compTwo, compTwoName) => `
export default ${compOne(compOneName)}
export ${compTwo(compTwoName)}`, // default export
// nb: module export at declaration time will be handled separately
// POST DECLARATION EXPORTS
(compOne, compOneName, compTwo, compTwoName) => `
Expand All @@ -133,16 +146,28 @@ const EXPORT_TYPES_INVALID = [
module.exports = { ${compOneName} ${compTwoName} }`, // module export, post declaration
];

// eslint-disable-next-line valid-jsdoc
/**
* @type {ComponentExportGenerator[]}
*/
const EXPORT_TYPES_INVALID_COMPLEX = [
// DECLARATION TIME EXPORTS
(compOne, compOneName, compTwo, compTwoName) => `
export default ${compOne(compOneName)}
export ${compTwo(compTwoName)}`, // default export
];

/**
* @param {ComponentExportGenerator[]} scenarioArray array of scenario generator functions which we will now convert into strings
* @param {ComponentGenerator[]} componentTypes array of components to generate on
* @param {boolean} [invalid] whether generated scenarios should expect to fail
* @returns {{code: string, options: object[], errors: object[]}[]}
*/
const generateScenarios = (scenarioArray, invalid = false) => {
const generateScenarios = (scenarioArray, componentTypes, invalid = false) => {
const result = [];
for (const scenario of scenarioArray) {
for (const first of COMPONENT_TYPES) {
for (const second of COMPONENT_TYPES) {
for (const first of componentTypes) {
for (const second of SIMPLE_COMPONENT_TYPES) {
result.push({
code: `
import React, { memo, Component } from 'react';
Expand All @@ -156,8 +181,14 @@ const generateScenarios = (scenarioArray, invalid = false) => {
return result;
};

const ignoreInternalValidScenarios = generateScenarios(EXPORT_TYPES_VALID);
const ignoreInternalInvalidScenarios = generateScenarios(EXPORT_TYPES_INVALID);
const ignoreInternalValidScenarios = [
...generateScenarios(EXPORT_TYPES_VALID, [...SIMPLE_COMPONENT_TYPES, ...COMPLEX_COMPONENT_TYPES]),
...generateScenarios(EXPORT_TYPES_VALID_COMPLEX, COMPLEX_COMPONENT_TYPES),
];
const ignoreInternalInvalidScenarios = [
...generateScenarios(EXPORT_TYPES_INVALID, [...SIMPLE_COMPONENT_TYPES, ...COMPLEX_COMPONENT_TYPES]),
...generateScenarios(EXPORT_TYPES_INVALID_COMPLEX, COMPLEX_COMPONENT_TYPES),
];

// ------------------------------------------------------------------------------
// Tests
Expand Down Expand Up @@ -412,6 +443,26 @@ ruleTester.run('no-multi-comp', rule, {
`,
options: [{ ignoreInternal: true }],
},
{ // basic testing for intersection of ignoreStateless and ignoreInternal
code: `
export function Hello(props) {
return <div>Hello {props.name}</div>;
}
export function HelloAgain(props) {
return <div>Hello again {props.name}</div>;
}
`,
options: [{ ignoreStateless: true, ignoreInternal: true }],
},
{
code: `
export const HelloComponent = (props) => {
return <div></div>;
}
export default React.memo((props, ref) => <HelloComponent {...props} />);
`,
options: [{ ignoreStateless: true, ignoreInternal: true }],
},
]),

invalid: parsers.all([
Expand Down

0 comments on commit cb7e496

Please sign in to comment.