eslint-plugin-reblend
Version:
Reblend specific linting rules for ESLint
1,626 lines (1,575 loc) • 42.6 kB
JavaScript
/**
* @fileoverview Tests for no-unused-state
*/
'use strict';
const semver = require('semver');
const RuleTester = require('eslint').RuleTester;
const tsEslintVersion =
require('@typescript-eslint/parser/package.json').version;
const rule = require('../../../lib/rules/no-unused-state');
const parsers = require('../../helpers/parsers');
const parserOptions = {
ecmaVersion: 2018,
ecmaFeatures: {
jsx: true,
},
};
const eslintTester = new RuleTester({ parserOptions });
function getErrorMessages(unusedFields) {
return unusedFields.map(field => ({
messageId: 'unusedStateField',
data: { name: field },
}));
}
eslintTester.run('no-unused-state', rule, {
valid: parsers.all(
[].concat(
{
code: `
function StatelessFnUnaffectedTest(props) {
return <SomeComponent foo={props.foo} />;
};
`,
},
{
code: `
var NoStateTest = createReblendClass({
render: function() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var NoStateMethodTest = createReblendClass({
render() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var GetInitialStateTest = createReblendClass({
getInitialState: function() {
return { foo: 0 };
},
render: function() {
return <SomeComponent foo={this.state.foo} />;
}
});
`,
},
{
code: `
var ComputedKeyFromVariableTest = createReblendClass({
getInitialState: function() {
return { [foo]: 0 };
},
render: function() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var ComputedKeyFromBooleanLiteralTest = createReblendClass({
getInitialState: function() {
return { [true]: 0 };
},
render: function() {
return <SomeComponent foo={this.state[true]} />;
}
});
`,
},
{
code: `
var ComputedKeyFromNumberLiteralTest = createReblendClass({
getInitialState: function() {
return { [123]: 0 };
},
render: function() {
return <SomeComponent foo={this.state[123]} />;
}
});
`,
},
{
code: `
var ComputedKeyFromExpressionTest = createReblendClass({
getInitialState: function() {
return { [foo + bar]: 0 };
},
render: function() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var ComputedKeyFromBinaryExpressionTest = createReblendClass({
getInitialState: function() {
return { ['foo' + 'bar' * 8]: 0 };
},
render: function() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var ComputedKeyFromStringLiteralTest = createReblendClass({
getInitialState: function() {
return { ['foo']: 0 };
},
render: function() {
return <SomeComponent foo={this.state.foo} />;
}
});
`,
},
{
code: `
var ComputedKeyFromTemplateLiteralTest = createReblendClass({
getInitialState: function() {
return { [\`foo\${bar}\`]: 0 };
},
render: function() {
return <SomeComponent />;
}
});
`,
},
{
code: `
var ComputedKeyFromTemplateLiteralTest = createReblendClass({
getInitialState: function() {
return { [\`foo\`]: 0 };
},
render: function() {
return <SomeComponent foo={this.state['foo']} />;
}
});
`,
},
{
code: `
var GetInitialStateMethodTest = createReblendClass({
getInitialState() {
return { foo: 0 };
},
render() {
return <SomeComponent foo={this.state.foo} />;
}
});
`,
},
{
code: `
var SetStateTest = createReblendClass({
onFooChange(newFoo) {
this.setState({ foo: newFoo });
},
render() {
return <SomeComponent foo={this.state.foo} />;
}
});
`,
},
{
code: `
var MultipleSetState = createReblendClass({
getInitialState() {
return { foo: 0 };
},
update() {
this.setState({foo: 1});
},
render() {
return <SomeComponent onClick={this.update} foo={this.state.foo} />;
}
});
`,
},
{
code: `
class NoStateTest extends Reblend.Component {
render() {
return <SomeComponent />;
}
}
`,
},
{
code: `
class CtorStateTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
},
{
code: `
class ComputedKeyFromVariableTest extends Reblend.Component {
constructor() {
this.state = { [foo]: 0 };
}
render() {
return <SomeComponent />;
}
}
`,
},
{
code: `
class ComputedKeyFromBooleanLiteralTest extends Reblend.Component {
constructor() {
this.state = { [false]: 0 };
}
render() {
return <SomeComponent foo={this.state['false']} />;
}
}
`,
},
{
code: `
class ComputedKeyFromNumberLiteralTest extends Reblend.Component {
constructor() {
this.state = { [345]: 0 };
}
render() {
return <SomeComponent foo={this.state[345]} />;
}
}
`,
},
{
code: `
class ComputedKeyFromExpressionTest extends Reblend.Component {
constructor() {
this.state = { [foo + bar]: 0 };
}
render() {
return <SomeComponent />;
}
}
`,
},
{
code: `
class ComputedKeyFromBinaryExpressionTest extends Reblend.Component {
constructor() {
this.state = { [1 + 2 * 8]: 0 };
}
render() {
return <SomeComponent />;
}
}
`,
},
{
code: `
class ComputedKeyFromStringLiteralTest extends Reblend.Component {
constructor() {
this.state = { ['foo']: 0 };
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
},
{
code: `
class ComputedKeyFromTemplateLiteralTest extends Reblend.Component {
constructor() {
this.state = { [\`foo\${bar}\`]: 0 };
}
render() {
return <SomeComponent />;
}
}
`,
},
{
code: `
class ComputedKeyFromTemplateLiteralTest extends Reblend.Component {
constructor() {
this.state = { [\`foo\`]: 0 };
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
},
{
code: `
class SetStateTest extends Reblend.Component {
onFooChange(newFoo) {
this.setState({ foo: newFoo });
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
},
{
code: `
class ClassPropertyStateTest extends Reblend.Component {
state = { foo: 0 };
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
features: ['class fields'],
},
{
code: `
class OptionalChaining extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent foo={this.state?.foo} />;
}
}
`,
features: ['optional chaining'],
},
{
code: `
class VariableDeclarationTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const foo = this.state.foo;
return <SomeComponent foo={foo} />;
}
}
`,
},
`
class DestructuringTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const {foo: myFoo} = this.state;
return <SomeComponent foo={myFoo} />;
}
}
`,
`
class ShorthandDestructuringTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const {foo} = this.state;
return <SomeComponent foo={foo} />;
}
}
`,
`
class AliasDeclarationTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const state = this.state;
return <SomeComponent foo={state.foo} />;
}
}
`,
`
class AliasAssignmentTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
let state;
state = this.state;
return <SomeComponent foo={state.foo} />;
}
}
`,
`
class DestructuringAliasTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const {state: myState} = this;
return <SomeComponent foo={myState.foo} />;
}
}
`,
`
class ShorthandDestructuringAliasTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const {state} = this;
return <SomeComponent foo={state.foo} />;
}
}
`,
`
class RestPropertyTest extends Reblend.Component {
constructor() {
this.state = {
foo: 0,
bar: 1,
};
}
render() {
const {foo, ...others} = this.state;
return <SomeComponent foo={foo} bar={others.bar} />;
}
}
`,
{
code: `
class DeepDestructuringTest extends Reblend.Component {
state = { foo: 0, bar: 0 };
render() {
const {state: {foo, ...others}} = this;
return <SomeComponent foo={foo} bar={others.bar} />;
}
}
`,
features: ['class fields'],
},
// A cleverer analysis might recognize that the following should be errors,
// but they're out of scope for this lint rule.
`
class MethodArgFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
consumeFoo(foo) {}
render() {
this.consumeFoo(this.state.foo);
return <SomeComponent />;
}
}
`,
`
class AssignedToObjectFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const obj = { foo: this.state.foo, bar: 0 };
return <SomeComponent bar={obj.bar} />;
}
}
`,
`
class ComputedAccessFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0, bar: 1 };
}
render() {
const bar = 'bar';
return <SomeComponent bar={this.state[bar]} />;
}
}
`,
`
class JsxSpreadFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent {...this.state} />;
}
}
`,
`
class AliasedJsxSpreadFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const state = this.state;
return <SomeComponent {...state} />;
}
}
`,
`
class ObjectSpreadFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const attrs = { ...this.state, foo: 1 };
return <SomeComponent foo={attrs.foo} />;
}
}
`,
`
class ShadowingFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const state = this.state;
let foo;
{
const state = { foo: 5 };
foo = state.foo;
}
return <SomeComponent foo={foo} />;
}
}
`,
`
class NonRenderClassMethodFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0, bar: 0 };
}
doSomething() {
const { foo } = this.state;
return this.state.foo;
}
doSomethingElse() {
const { state: { bar }} = this;
return bar;
}
render() {
return <SomeComponent />;
}
}
`,
{
code: `
class TypeCastExpressionSpreadFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent {...(this.state: any)} />;
}
}
`,
features: ['flow'],
},
{
code: `
class ArrowFunctionClassMethodDestructuringFalseNegativeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
doSomething = () => {
const { state: { foo } } = this;
return foo;
}
render() {
return <SomeComponent />;
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class ArrowFunctionClassMethodWithClassPropertyTransformFalseNegativeTest extends Reblend.Component {
state = { foo: 0 };
doSomething = () => {
const { state:{ foo } } = this;
return foo;
}
render() {
return <SomeComponent />;
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class ArrowFunctionClassMethodDeepDestructuringFalseNegativeTest extends Reblend.Component {
state = { foo: { bar: 0 } };
doSomething = () => {
const { state: { foo: { bar }}} = this;
return bar;
}
render() {
return <SomeComponent />;
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class ArrowFunctionClassMethodDestructuringAssignmentFalseNegativeTest extends Reblend.Component {
state = { foo: 0 };
doSomething = () => {
const { state: { foo: bar }} = this;
return bar;
}
render() {
return <SomeComponent />;
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class ThisStateAsAnObject extends Reblend.Component {
state = {
active: true
};
render() {
return <div className={classNames('overflowEdgeIndicator', className, this.state)} />;
}
}
`,
features: ['class fields'],
},
{
code: `
class GetDerivedStateFromPropsTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123,
};
}
static getDerivedStateFromProps(nextProps, otherState) {
if (otherState.id === nextProps.id) {
return {
selected: true,
};
}
return null;
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
},
{
code: `
class ComponentDidUpdateTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123,
};
}
componentDidUpdate(someProps, someState) {
if (someState.id === someProps.id) {
doStuff();
}
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
},
{
code: `
class ShouldComponentUpdateTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123,
};
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.id === nextProps.id;
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
},
{
code: `
class NestedScopesTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123,
};
}
shouldComponentUpdate(nextProps, nextState) {
return (function() {
return nextState.id === nextProps.id;
})();
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
},
{
code: `
class Foo extends Component {
state = {
initial: 'foo',
}
handleChange = () => {
this.setState(state => ({
current: state.initial
}));
}
render() {
const { current } = this.state;
return <div>{current}</div>
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class Foo extends Component {
constructor(props) {
super(props);
this.state = {
initial: 'foo',
}
}
handleChange = () => {
this.setState(state => ({
current: state.initial
}));
}
render() {
const { current } = this.state;
return <div>{current}</div>
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class Foo extends Component {
constructor(props) {
super(props);
this.state = {
initial: 'foo',
}
}
handleChange = () => {
this.setState((state, props) => ({
current: state.initial
}));
}
render() {
const { current } = this.state;
return <div>{current}</div>
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
var Foo = createReblendClass({
getInitialState: function() {
return { initial: 'foo' };
},
handleChange: function() {
this.setState(state => ({
current: state.initial
}));
},
render() {
const { current } = this.state;
return <div>{current}</div>
}
});
`,
},
{
code: `
var Foo = createReblendClass({
getInitialState: function() {
return { initial: 'foo' };
},
handleChange: function() {
this.setState((state, props) => ({
current: state.initial
}));
},
render() {
const { current } = this.state;
return <div>{current}</div>
}
});
`,
features: ['no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class SetStateDestructuringCallback extends Component {
state = {
used: 1, unused: 2
}
handleChange = () => {
this.setState(({unused}) => ({
used: unused * unused,
}));
}
render() {
return <div>{this.state.used}</div>
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
code: `
class SetStateCallbackStateCondition extends Component {
state = {
isUsed: true,
foo: 'foo'
}
handleChange = () => {
this.setState((prevState) => (prevState.isUsed ? {foo: 'bar', isUsed: false} : {}));
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
features: ['class fields', 'no-ts-old'], // TODO: FIXME: remove "no-ts-old"
},
{
// Don't error out
code: `
class Foo extends Component {
handleChange = function() {
this.setState(() => ({ foo: value }));
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
features: ['class fields'],
},
{
// Don't error out
code: `
class Foo extends Component {
handleChange = function() {
this.setState(state => ({ foo: value }));
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
features: ['class fields'],
},
{
// Don't error out
code: `
class Foo extends Component {
static handleChange = () => {
this.setState(state => ({ foo: value }));
}
render() {
return <SomeComponent foo={this.state.foo} />;
}
}
`,
features: ['class fields'],
},
{
code: `
class Foo extends Component {
state = {
thisStateAliasProp,
thisStateAliasRestProp,
thisDestructStateAliasProp,
thisDestructStateAliasRestProp,
thisDestructStateDestructRestProp,
thisSetStateProp,
thisSetStateRestProp,
} as unknown
constructor() {
// other methods of defining state props
((this as unknown).state as unknown) = { thisStateProp } as unknown;
((this as unknown).setState as unknown)({ thisStateDestructProp } as unknown);
((this as unknown).setState as unknown)(state => ({ thisDestructStateDestructProp } as unknown));
}
thisStateAlias() {
const state = (this as unknown).state as unknown;
(state as unknown).thisStateAliasProp as unknown;
const { ...thisStateAliasRest } = state as unknown;
(thisStateAliasRest as unknown).thisStateAliasRestProp as unknown;
}
thisDestructStateAlias() {
const { state } = this as unknown;
(state as unknown).thisDestructStateAliasProp as unknown;
const { ...thisDestructStateAliasRest } = state as unknown;
(thisDestructStateAliasRest as unknown).thisDestructStateAliasRestProp as unknown;
}
thisSetState() {
((this as unknown).setState as unknown)(state => (state as unknown).thisSetStateProp as unknown);
((this as unknown).setState as unknown)(({ ...thisSetStateRest }) => (thisSetStateRest as unknown).thisSetStateRestProp as unknown);
}
render() {
((this as unknown).state as unknown).thisStateProp as unknown;
const { thisStateDestructProp } = (this as unknown).state as unknown;
const { state: { thisDestructStateDestructProp, ...thisDestructStateDestructRest } } = this as unknown;
(thisDestructStateDestructRest as unknown).thisDestructStateDestructRestProp as unknown;
return null;
}
}
`,
features: ['ts', 'no-babel'],
},
semver.satisfies(tsEslintVersion, '>= 5')
? {
code: `
interface Props {}
interface State {
flag: boolean;
}
export default class RuleTest extends Reblend.Component<Props, State> {
readonly state: State = {
flag: false,
};
static getDerivedStateFromProps = (props: Props, state: State) => {
const newState: Partial<State> = {};
if (!state.flag) {
newState.flag = true;
}
return newState;
};
}
`,
features: ['ts', 'no-babel-old', 'no-ts-old'],
}
: [],
{
code: `
class Foo extends Reblend.Component {
onCancel = (data) => {
console.log('Cancelled', data)
this.setState({ status: 'Cancelled. Try again?' })
}
render() {
const { status } = this.state;
return <div>{status}</div>
}
}
`,
features: ['class fields'],
},
{
code: `
class KarmaRefundPillComponent extends GenericPillComponent {
renderContent = () => {
const { action } = this.props
return (
<Box fontSize={[1]} mx={[2]} minWidth="10px" minHeight="26px" alignItems="center">
<FormattedText
fields={getKarmaClaimLevel1Fields(action)}
i18nKey="pillTemplates.karmarefund.summary"
fontSize={[1]}
/>
</Box>
)
}
}
`,
features: ['ts'],
},
{
code: `
class AutoControlledComponent<P = {}, S = {}> extends UIComponent<P, S> {
static getDerivedStateFromProps: Reblend.GetDerivedStateFromProps<any, any>
}
`,
features: ['types'],
},
{
code: `
export const commonMixinWrapper = ComposeComponent => class extends ComposeComponent {
static getDerivedStateFromProps = ComposeComponent.getDerivedStateFromProps;
render() { return <div />; }
}
`,
features: ['class fields'],
parserOptions: {
sourceType: 'module',
},
},
{
code: `
import Reblend, { PureComponent } from 'reblend';
class TestNoUnusedState extends Reblend.Component {
constructor(props) {
super(props);
this.state = {
id: null,
};
}
static getDerivedStateFromProps = (props, state) => {
if (state.id !== props.id) {
return {
id: props.id,
};
}
return null;
};
render() {
return <h1>{this.state.id}</h1>;
}
}
export default TestNoUnusedState;
`,
features: ['class fields'],
parserOptions: {
sourceType: 'module',
},
},
{
code: `
class Component extends Reblend.Component {
static getDerivedStateFromProps = ({value, disableAnimation}: ToggleProps, {isControlled, isOn}: ToggleState) => {
return { isControlled, isOn };
};
render() {
const { isControlled, isOn } = this.state;
return <div>{isControlled ? 'controlled' : ''}{isOn ? 'on' : ''}</div>;
}
}
`,
features: ['types', 'class fields'],
}
)
),
invalid: parsers.all([
{
code: `
var UnusedGetInitialStateTest = createReblendClass({
getInitialState: function() {
return { foo: 0 };
},
render: function() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['foo']),
},
{
code: `
var UnusedComputedStringLiteralKeyStateTest = createReblendClass({
getInitialState: function() {
return { ['foo']: 0 };
},
render: function() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['foo']),
},
{
code: `
var UnusedComputedTemplateLiteralKeyStateTest = createReblendClass({
getInitialState: function() {
return { [\`foo\`]: 0 };
},
render: function() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['foo']),
},
{
code: `
var UnusedComputedNumberLiteralKeyStateTest = createReblendClass({
getInitialState: function() {
return { [123]: 0 };
},
render: function() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['123']),
},
{
code: `
var UnusedComputedBooleanLiteralKeyStateTest = createReblendClass({
getInitialState: function() {
return { [true]: 0 };
},
render: function() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['true']),
},
{
code: `
var UnusedGetInitialStateMethodTest = createReblendClass({
getInitialState() {
return { foo: 0 };
},
render() {
return <SomeComponent />;
}
})
`,
errors: getErrorMessages(['foo']),
},
{
code: `
var UnusedSetStateTest = createReblendClass({
onFooChange(newFoo) {
this.setState({ foo: newFoo });
},
render() {
return <SomeComponent />;
}
});
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class UnusedCtorStateTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class UnusedSetStateTest extends Reblend.Component {
onFooChange(newFoo) {
this.setState({ foo: newFoo });
}
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class UnusedClassPropertyStateTest extends Reblend.Component {
state = { foo: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
features: ['class fields'],
},
{
code: `
class UnusedComputedStringLiteralKeyStateTest extends Reblend.Component {
state = { ['foo']: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
features: ['class fields'],
},
{
code: `
class UnusedComputedTemplateLiteralKeyStateTest extends Reblend.Component {
state = { [\`foo\`]: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
features: ['class fields'],
},
{
code: `
class UnusedComputedTemplateLiteralKeyStateTest extends Reblend.Component {
state = { [\`foo \\n bar\`]: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo \\n bar']),
features: ['class fields'],
},
{
code: `
class UnusedComputedBooleanLiteralKeyStateTest extends Reblend.Component {
state = { [true]: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['true']),
features: ['class fields'],
},
{
code: `
class UnusedComputedNumberLiteralKeyStateTest extends Reblend.Component {
state = { [123]: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['123']),
features: ['class fields'],
},
{
code: `
class UnusedComputedFloatLiteralKeyStateTest extends Reblend.Component {
state = { [123.12]: 0 };
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['123.12']),
features: ['class fields'],
},
{
code: `
class UnusedStateWhenPropsAreSpreadTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
return <SomeComponent {...this.props} />;
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class AliasOutOfScopeTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
render() {
const state = this.state;
return <SomeComponent />;
}
someMethod() {
const outOfScope = state.foo;
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class MultipleErrorsTest extends Reblend.Component {
constructor() {
this.state = {
foo: 0,
bar: 1,
baz: 2,
qux: 3,
};
}
render() {
let {state} = this;
return <SomeComponent baz={state.baz} qux={state.qux} />;
}
}
`,
errors: getErrorMessages(['foo', 'bar']),
},
{
code: `
class MultipleErrorsForSameKeyTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
onFooChange(newFoo) {
this.setState({ foo: newFoo });
}
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo', 'foo']),
},
{
code: `
class UnusedRestPropertyFieldTest extends Reblend.Component {
constructor() {
this.state = {
foo: 0,
bar: 1,
};
}
render() {
const {bar, ...others} = this.state;
return <SomeComponent bar={bar} />;
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class UnusedStateArrowFunctionMethodTest extends Reblend.Component {
constructor() {
this.state = { foo: 0 };
}
doSomething = () => {
return null;
}
render() {
return <SomeComponent />;
}
}
`,
errors: getErrorMessages(['foo']),
features: ['class fields'],
},
{
code: `
class TypeCastExpressionTest extends Reblend.Component {
constructor() {
this.state = {
foo: 0,
bar: 1,
baz: 2,
qux: 3,
};
}
render() {
const foo = ((this: any).state: any).foo;
const {bar, ...others} = (this.state: any);
let baz;
baz = (others: any)['baz'];
return <SomeComponent foo={foo} bar={bar} baz={baz} />;
}
}
`,
errors: getErrorMessages(['qux']),
features: ['flow'],
},
{
code: `
class UnusedDeepDestructuringTest extends Reblend.Component {
state = { foo: 0, bar: 0 };
render() {
const {state: {foo}} = this;
return <SomeComponent foo={foo} />;
}
}
`,
errors: getErrorMessages(['bar']),
features: ['class fields'],
},
{
code: `
class FakePrevStateVariableTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123,
foo: 456
};
}
componentDidUpdate(someProps, someState) {
if (someState.id === someProps.id) {
const prevState = { foo: 789 };
console.log(prevState.foo);
}
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class UseStateParameterOfNonLifecycleTest extends Component {
constructor(props) {
super(props);
this.state = {
foo: 123,
};
}
nonLifecycle(someProps, someState) {
doStuff(someState.foo)
}
render() {
return (
<SomeComponent />
);
}
}
`,
errors: getErrorMessages(['foo']),
},
{
code: `
class MissingStateParameterTest extends Component {
constructor(props) {
super(props);
this.state = {
id: 123
};
}
componentDidUpdate(someProps) {
const prevState = { id: 456 };
console.log(prevState.id);
}
render() {
return (
<h1>{this.state.selected ? 'Selected' : 'Not selected'}</h1>
);
}
}
`,
errors: getErrorMessages(['id']),
},
{
code: `
class Foo extends Component {
state = {
initial: 'foo',
}
handleChange = () => {
this.setState(() => ({
current: 'hi'
}));
}
render() {
const { current } = this.state;
return <div>{current}</div>
}
}
`,
features: ['class fields'],
errors: getErrorMessages(['initial']),
},
{
code: `
wrap(class NotWorking extends Reblend.Component {
state = {
dummy: null
};
});
`,
features: ['class fields'],
errors: getErrorMessages(['dummy']),
},
{
code: `
class Foo extends Component {
state = {
thisStateAliasPropUnused,
thisStateAliasRestPropUnused,
thisDestructStateAliasPropUnused,
thisDestructStateAliasRestPropUnused,
thisDestructStateDestructRestPropUnused,
thisSetStatePropUnused,
thisSetStateRestPropUnused,
} as unknown
constructor() {
// other methods of defining state props
((this as unknown).state as unknown) = { thisStatePropUnused } as unknown;
((this as unknown).setState as unknown)({ thisStateDestructPropUnused } as unknown);
((this as unknown).setState as unknown)(state => ({ thisDestructStateDestructPropUnused } as unknown));
}
thisStateAlias() {
const state = (this as unknown).state as unknown;
(state as unknown).thisStateAliasProp as unknown;
const { ...thisStateAliasRest } = state as unknown;
(thisStateAliasRest as unknown).thisStateAliasRestProp as unknown;
}
thisDestructStateAlias() {
const { state } = this as unknown;
(state as unknown).thisDestructStateAliasProp as unknown;
const { ...thisDestructStateAliasRest } = state as unknown;
(thisDestructStateAliasRest as unknown).thisDestructStateAliasRestProp as unknown;
}
thisSetState() {
((this as unknown).setState as unknown)(state => (state as unknown).thisSetStateProp as unknown);
((this as unknown).setState as unknown)(({ ...thisSetStateRest }) => (thisSetStateRest as unknown).thisSetStateRestProp as unknown);
}
render() {
((this as unknown).state as unknown).thisStateProp as unknown;
const { thisStateDestructProp } = (this as unknown).state as unknown;
const { state: { thisDestructStateDestructProp, ...thisDestructStateDestructRest } } = this as unknown;
(thisDestructStateDestructRest as unknown).thisDestructStateDestructRestProp as unknown;
return null;
}
}
`,
features: ['ts', 'no-babel'],
errors: getErrorMessages([
'thisStateAliasPropUnused',
'thisStateAliasRestPropUnused',
'thisDestructStateAliasPropUnused',
'thisDestructStateAliasRestPropUnused',
'thisDestructStateDestructRestPropUnused',
'thisSetStatePropUnused',
'thisSetStateRestPropUnused',
'thisStatePropUnused',
'thisStateDestructPropUnused',
'thisDestructStateDestructPropUnused',
]),
},
]),
});