eslint-plugin-reblend
Version:
Reblend specific linting rules for ESLint
2,077 lines (2,041 loc) • 71.7 kB
JavaScript
/**
* @fileoverview Enforce a defaultProps definition for every prop that is not a required prop.
* @author Vitor Balocco
*/
'use strict';
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const RuleTester = require('eslint').RuleTester;
const rule = require('../../../lib/rules/require-default-props');
const parsers = require('../../helpers/parsers');
const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
};
const ruleTester = new RuleTester({ parserOptions });
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
ruleTester.run('require-default-props', rule, {
valid: parsers.all([
// stateless components as function declarations
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired
};
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
bar: PropTypes.string.isRequired
};
MyStatelessComponent.propTypes.foo = PropTypes.string;
MyStatelessComponent.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
bar: PropTypes.string.isRequired
};
MyStatelessComponent.propTypes.foo = PropTypes.string;
MyStatelessComponent.defaultProps = {};
MyStatelessComponent.defaultProps.foo = "foo";
`,
},
{
code: `
function MyStatelessComponent({ foo }) {
return <div>{foo}</div>;
}
MyStatelessComponent.propTypes = {};
MyStatelessComponent.propTypes.foo = PropTypes.string;
MyStatelessComponent.defaultProps = {};
MyStatelessComponent.defaultProps.foo = "foo";
`,
},
{
code: `
const types = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
const defaults = {
foo: "foo"
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = defaults;
`,
},
{
code: `
const defaults = {
foo: "foo"
};
const types = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
function MyStatelessComponent({ foo = "test", bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
function MyStatelessComponent({ foo = "test", bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
};
`,
options: [
{
forbidDefaultForRequired: true,
ignoreFunctionalComponents: true,
},
],
},
{
code: `
export function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
export default function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
function Hello(props) {
return <div>Hello {props.foo}</div>;
}
Hello.propTypes = {
foo: PropTypes.string
};
Hello.defaultProps = {
foo: 'bar;'
}
`,
options: [{ forbidDefaultForRequired: true }],
},
{
code: `
function Hello({ foo = 'asdf', bar }) {
return <div>Hello {foo} and {bar}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
function Hello({ foo = 'asdf', bar = 'qwer' }) {
return <div>Hello {foo} and {bar}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
function Hello(props) {
return <div>Hello {foo} and {bar}</div>;
}
Hello.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired,
};
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
function MyStatelessComponent({ foo, bar = 'asdf', ...props }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string,
hello: PropTypes.string.isRequired,
world: PropTypes.string.isRequired
}
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
function MyStatelessComponent({ foo, bar, ...props }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired,
hello: PropTypes.string.isRequired,
world: PropTypes.string.isRequired
}
`,
options: [{ functions: 'defaultArguments' }],
},
// stateless components as function expressions
{
code: `
import PropTypes from 'prop-types';
import Reblend from 'reblend';
const MyComponent = function({ foo, bar }) {
return <div>{foo}{bar}</div>;
};
MyComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
export default MyComponent;
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
import PropTypes from 'prop-types';
import Reblend from 'reblend';
export const MyComponent = function({ foo, bar }) {
return <div>{foo}{bar}</div>;
};
MyComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
const Hello = function({ foo = 'asdf', bar = 'qwer' }) {
return <div>Hello {foo} and {bar}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
const NoPropsComponent = function () {
return <div>Hello, world!</div>;
}
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
function NoPropsComponent() {
return <div>Hello, world!</div>;
}
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
const NoPropsComponent = () => {
return <div>Hello, world!</div>;
}
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
const NoPropsComponent = function () {
return <div>Hello, world!</div>;
}
NoPropsComponent.propTypes = {};
`,
options: [{ functions: 'defaultArguments' }],
},
// stateless components as arrow function expressions
{
code: `
import PropTypes from 'prop-types';
import Reblend from 'reblend';
const MyComponent = ({ foo, bar }) => {
return <div>{foo}{bar}</div>;
};
MyComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
export default MyComponent;
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
import PropTypes from 'prop-types';
import Reblend from 'reblend';
export const MyComponent = ({ foo, bar }) => {
return <div>{foo}{bar}</div>;
};
MyComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
export default MyComponent;
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
const Hello = ({ foo = 'asdf', bar = 'qwer' }) => {
return <div>Hello {foo} and {bar}</div>;
}
Hello.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
`,
options: [{ functions: 'defaultArguments' }],
},
// createReblendClass components
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired
}
});
`,
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
},
getDefaultProps: function() {
return {
foo: "foo"
};
}
});
`,
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string
},
getDefaultProps: function() {
return {
foo: "foo",
bar: "bar"
};
}
});
`,
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
}
});
`,
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string
},
getDefaultProps: function() {
return {
foo: "foo",
bar: "bar"
};
}
});
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string
},
getDefaultProps: function() {
return {
foo: "foo",
bar: "bar"
};
}
});
`,
options: [{ functions: 'defaultArguments' }],
},
// ES6 class component
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.string.isRequired
};
Greeting.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
Greeting.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: PropTypes.string.isRequired
};
Greeting.propTypes.foo = PropTypes.string;
Greeting.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: PropTypes.string.isRequired
};
Greeting.propTypes.foo = PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {};
Greeting.propTypes.foo = PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
Greeting.defaultProps = {
foo: "foo"
};
`,
options: [{ ignoreFunctionalComponents: true }],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
Greeting.defaultProps = {
foo: "foo"
};
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ classes: 'ignore' }],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
Greeting.defaultProps = {
foo: "foo"
};
`,
options: [{ classes: 'ignore' }],
},
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
name: PropTypes.string
};
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
options: [{ classes: 'ignore' }],
},
// edge cases
// not a reblend component
{
code: `
function NotAComponent({ foo, bar }) {}
NotAComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
},
{
code: `
class Greeting {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: PropTypes.string.isRequired
};
`,
},
{
code: `
let Greetings = {};
Greetings.Hello = class extends Reblend.Component {
render () {
return <div>Hello {this.props.foo}</div>;
}
}
Greetings.Hello.propTypes = {
foo: PropTypes.string
};
`,
options: [{ classes: 'ignore' }],
},
// external references
{
code: `
const defaults = require("./defaults");
const types = {
foo: PropTypes.string,
bar: PropTypes.string
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
},
{
code: `
const defaults = {
foo: "foo"
};
const types = require("./propTypes");
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
},
{
code: `
MyStatelessComponent.propTypes = {
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = require("./defaults").foo;
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
MyStatelessComponent.propTypes = {
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = require("./defaults").foo;
MyStatelessComponent.defaultProps.bar = "bar";
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
import defaults from "./defaults";
MyStatelessComponent.propTypes = {
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = defaults;
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
parserOptions: Object.assign({ sourceType: 'module' }, parserOptions),
},
{
code: `
import { foo } from "./defaults";
MyStatelessComponent.propTypes = {
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = foo;
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
parserOptions: Object.assign({ sourceType: 'module' }, parserOptions),
},
// using spread operator
{
code: `
const component = rowsOfType(GuestlistEntry, (rowData, ownProps) => ({
...rowData,
onPress: () => ownProps.onPress(rowData.id),
}));
`,
},
{
code: `
MyStatelessComponent.propTypes = {
...stuff,
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = {
foo: "foo"
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
MyStatelessComponent.propTypes = {
foo: PropTypes.string
};
MyStatelessComponent.defaultProps = {
...defaults,
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
MyStatelessComponent.propTypes = {
...stuff,
foo: PropTypes.string
};
function MyStatelessComponent({ foo = "foo", bar }) {
return <div>{foo}{bar}</div>;
}
`,
options: [{ functions: 'defaultArguments' }],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
...someProps,
bar: PropTypes.string.isRequired
};
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
Greeting.defaultProps = {
...defaults,
bar: "bar"
};
`,
},
// with Flow annotations
{
code: `
type Props = {
foo: string
};
class Hello extends Reblend.Component {
props: Props;
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['types'],
},
{
code: `
type Props = {
foo: string,
bar?: string
};
class Hello extends Reblend.Component {
props: Props;
render() {
return <div>Hello {this.props.foo}</div>;
}
}
Hello.defaultProps = {
bar: "bar"
};
`,
features: ['types'],
},
{
code: `
class Hello extends Reblend.Component {
props: {
foo: string,
bar?: string
};
render() {
return <div>Hello {this.props.foo}</div>;
}
}
Hello.defaultProps = {
bar: "bar"
};
`,
features: ['types'],
},
{
code: `
class Hello extends Reblend.Component {
props: {
foo: string
};
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['types'],
},
{
code: `
function Hello(props: { foo?: string }) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = { foo: "foo" };
`,
features: ['types'],
},
{
code: `
function Hello(props: { foo: string }) {
return <div>Hello {foo}</div>;
}
`,
features: ['types'],
},
{
code: `
const Hello = (props: { foo?: string }) => {
return <div>Hello {props.foo}</div>;
};
Hello.defaultProps = { foo: "foo" };
`,
features: ['types'],
},
{
code: `
const Hello = (props: { foo: string }) => {
return <div>Hello {foo}</div>;
};
`,
features: ['types'],
},
{
code: `
const Hello = function(props: { foo?: string }) {
return <div>Hello {props.foo}</div>;
};
Hello.defaultProps = { foo: "foo" };
`,
features: ['types'],
},
{
code: `
const Hello = function(props: { foo: string }) {
return <div>Hello {foo}</div>;
};
`,
features: ['types'],
},
{
code: `
type Props = {
foo: string,
bar?: string
};
type Props2 = {
foo: string,
baz?: string
}
function Hello(props: Props | Props2) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = {
bar: "bar",
baz: "baz"
};
`,
features: ['types'],
},
{
code: `
import type Props from "fake";
class Hello extends Reblend.Component {
props: Props;
render () {
return <div>Hello {this.props.name.firstname}</div>;
}
}
`,
features: ['types'],
},
{
code: `
type Props = any;
const Hello = function({ foo }: Props) {
return <div>Hello {foo}</div>;
};
`,
features: ['types'],
},
{
code: `
import type ImportedProps from "fake";
type Props = ImportedProps;
function Hello(props: Props) {
return <div>Hello {props.name.firstname}</div>;
}
`,
features: ['types'],
},
// don't error when variable is not in scope
{
code: `
import type { ImportedType } from "fake";
type Props = ImportedType;
function Hello(props: Props) {
return <div>Hello {props.name.firstname}</div>;
}
`,
features: ['types'],
},
// make sure error is not thrown with multiple assignments
{
code: `
import type ImportedProps from "fake";
type NestedProps = ImportedProps;
type Props = NestedProps;
function Hello(props: Props) {
return <div>Hello {props.name.firstname}</div>;
}
`,
features: ['types'],
},
// make sure defaultProps are correctly detected with quoted properties
{
code: `
function Hello(props) {
return <div>Hello {props.bar}</div>;
}
Hello.propTypes = {
bar: PropTypes.string
};
Hello.defaultProps = {
"bar": "bar"
};
`,
},
{
code: `
class Hello extends Reblend.Component {
static propTypes = {
foo: PropTypes.string.isRequired
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['class fields'],
options: [{ forbidDefaultForRequired: true }],
},
// test support for Reblend PropTypes as Component's class generic
{
code: `
type HelloProps = {
foo: string,
bar?: string
};
class Hello extends Reblend.Component<HelloProps> {
static defaultProps = {
bar: "bar"
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['flow'],
options: [{ forbidDefaultForRequired: true }],
},
{
code: `
type HelloProps = {
foo: string,
bar?: string
};
class Hello extends Component<HelloProps> {
static defaultProps = {
bar: "bar"
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['flow'],
options: [{ forbidDefaultForRequired: true }],
},
{
code: `
type HelloProps = {
foo: string,
bar?: string
};
type HelloState = {
dummyState: string
};
class Hello extends Component<HelloProps, HelloState> {
static defaultProps = {
bar: "bar"
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['flow'],
options: [{ forbidDefaultForRequired: true }],
},
{
code: `
type HelloProps = {
foo?: string,
bar?: string
};
class Hello extends Component<HelloProps> {
static defaultProps = {
foo: "foo",
bar: "bar"
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
features: ['flow'],
options: [{ forbidDefaultForRequired: true }],
},
{
code: `
type Props = {
foo?: string,
bar?: string
}
function Hello({ foo = 'asdf', bar = 'qwer' }: Props) {
return <div>Hello {foo} and {bar}</div>;
}
`,
features: ['types'],
options: [{ functions: 'defaultArguments' }],
},
{
code: `
type Props = {
foo?: string,
bar: string
}
function Hello({ foo = 'asdf', bar }: Props) {
return <div>Hello {foo} and {bar}</div>;
}
`,
features: ['types'],
options: [{ functions: 'defaultArguments' }],
},
{
code: `
type Props = {
+name?: string,
};
function Hello(props: Props) {
return <div>Hello {props.name}</div>;
}
Hello.defaultProps = {
name: 'foo'
};
`,
features: ['flow'],
},
{
code: `
import Reblend from "reblend";
interface Props {
name: string;
}
const MyComponent: Reblend.FC<Props> = ({ name }) => {
return <div>{name}</div>;
};
export default MyComponent;
`,
features: ['ts'],
},
{
code: `
interface Props {
foo?: string,
bar?: string
}
const Hello: Reblend.FC<Props> = ({ foo = 'asdf', bar = 'qwer' }) => {
return <div>Hello {foo} and {bar}</div>;
}
`,
features: ['ts'],
options: [{ functions: 'defaultArguments' }],
},
]),
invalid: parsers.all([
// stateless components
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = forbidExtraProps({
foo: PropTypes.string,
bar: PropTypes.string.isRequired
});
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
],
settings: {
propWrapperFunctions: ['forbidExtraProps'],
},
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
const propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
MyStatelessComponent.propTypes = forbidExtraProps(propTypes);
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
],
settings: {
propWrapperFunctions: ['forbidExtraProps'],
},
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
MyStatelessComponent.propTypes.baz = Reblend.propTypes.string;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
{
messageId: 'shouldHaveDefault',
data: { name: 'baz' },
line: 9,
column: 9,
},
],
},
{
code: `
const types = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 3,
column: 11,
},
],
},
{
code: `
const defaults = {
foo: "foo"
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
MyStatelessComponent.defaultProps = defaults;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 10,
column: 11,
},
],
},
{
code: `
const defaults = {
foo: "foo"
};
const types = {
foo: PropTypes.string,
bar: PropTypes.string
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 7,
column: 11,
},
],
},
{
code: `
const types = {
foo: PropTypes.string,
bar: PropTypes.string
};
function MyStatelessComponent({ foo, bar = "asdf" }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
`,
options: [{ functions: 'defaultArguments' }],
errors: [
{
messageId: 'shouldAssignObjectDefault',
data: { name: 'foo' },
line: 6,
column: 41,
},
],
},
{
code: `
const types = {
foo: PropTypes.string,
};
function MyStatelessComponent(props) {
return <div>{props.foo}</div>;
}
MyStatelessComponent.propTypes = types;
`,
options: [{ functions: 'defaultArguments' }],
errors: [
{
messageId: 'destructureInSignature',
line: 5,
column: 39,
},
],
},
{
code: `
function MyStatelessComponent({ foo, bar, ...props }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired,
hello: PropTypes.string.isRequired,
world: PropTypes.string.isRequired
}
`,
options: [{ functions: 'defaultArguments' }],
errors: [
{
messageId: 'shouldAssignObjectDefault',
data: { name: 'foo' },
line: 2,
column: 41,
},
],
},
// createReblendClass components
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
}
});
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 7,
column: 13,
},
],
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
}
});
`,
options: [{ ignoreFunctionalComponents: true }],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 7,
column: 13,
},
],
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string
},
getDefaultProps: function() {
return {
foo: "foo"
};
}
});
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 8,
column: 13,
},
],
},
// ES6 class component
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 10,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
`,
options: [{ ignoreFunctionalComponents: true }],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 10,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
Greeting.defaultProps = {
foo: "foo"
};
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 11,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: PropTypes.string.isRequired
};
Greeting.propTypes.foo = PropTypes.string;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 12,
column: 9,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: PropTypes.string
};
Greeting.propTypes.foo = PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 10,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {};
Greeting.propTypes.foo = PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.bar = "bar";
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 10,
column: 9,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
const props = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
Greeting.propTypes = props;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 10,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
const props = {
foo: PropTypes.string,
bar: PropTypes.string
};
const defaults = {
foo: "foo"
};
Greeting.propTypes = props;
Greeting.defaultProps = defaults;
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 11,
column: 11,
},
],
},
// ES6 classes with static getter methods
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
name: PropTypes.string
};
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'name' },
line: 5,
column: 15,
},
],
},
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
name: PropTypes.string
};
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
options: [{ ignoreFunctionalComponents: true }],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'name' },
line: 5,
column: 15,
},
],
},
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
foo: PropTypes.string,
bar: PropTypes.string
};
}
static get defaultProps() {
return {
bar: "world"
};
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 5,
column: 15,
},
],
},
{
code: `
const props = {
foo: PropTypes.string
};
class Hello extends Reblend.Component {
static get propTypes() {
return props;
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 3,
column: 11,
},
],
},
{
code: `
const defaults = {
bar: "world"
};
class Hello extends Reblend.Component {
static get propTypes() {
return {
foo: PropTypes.string,
bar: PropTypes.string
};
}
static get defaultProps() {
return defaults;
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 8,
column: 15,
},
],
},
// ES6 classes with property initializers
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
}
`,
features: ['class fields'],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 9,
column: 13,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
}
`,
features: ['class fields'],
options: [{ ignoreFunctionalComponents: true }],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 9,
column: 13,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = {
foo: PropTypes.string,
bar: PropTypes.string
};
static defaultProps = {
foo: "foo"
};
}
`,
features: ['class fields'],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 10,
column: 13,
},
],
},
{
code: `
const props = {
foo: PropTypes.string,
bar: PropTypes.string.isRequired
};
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = props;
}
`,
features: ['class fields'],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 3,
column: 11,
},
],
},
{
code: `
const props = {
foo: PropTypes.string,
bar: PropTypes.string
};
const defaults = {
foo: "foo"
};
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = props;
static defaultProps = defaults;
}
`,
features: ['class fields'],
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'bar' },
line: 4,
column: 11,
},
],
},
// edge cases
{
code: `
let Greetings = {};
Greetings.Hello = class extends Reblend.Component {
render () {
return <div>Hello {this.props.foo}</div>;
}
}
Greetings.Hello.propTypes = {
foo: PropTypes.string
};
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 9,
column: 11,
},
],
},
{
code: `
var Greetings = ({ foo = "foo" }) => {
return <div>Hello {this.props.foo}</div>;
}
Greetings.propTypes = {
foo: PropTypes.string
};
`,
errors: [
{
messageId: 'shouldHaveDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
],
},
// component with no declared props followed by a failing component
{
code: `
var ComponentWithNoProps = ({ bar = "bar" }) => {
return <div>Hello {this.props.foo}</div>;
}
var Greetings = ({ foo = "foo" }) => {
return <div>Hello {this.props.foo}</div>;
}
Greetings.propTypes = {
foo: PropTypes.stri