eslint-plugin-reblend
Version:
Reblend specific linting rules for ESLint
1,786 lines (1,712 loc) • 41.5 kB
JavaScript
/**
* @fileoverview Enforce all defaultProps are declared and non-required propTypes
* @author Vitor Balocco
* @author Roy Sutton
*/
'use strict';
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const RuleTester = require('eslint').RuleTester;
const rule = require('../../../lib/rules/default-props-match-prop-types');
const parsers = require('../../helpers/parsers');
const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
};
const ruleTester = new RuleTester({ parserOptions });
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
ruleTester.run('default-props-match-prop-types', rule, {
valid: parsers.all([
// stateless components
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string.isRequired
};
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.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: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.propTypes.foo = Reblend.PropTypes.string;
MyStatelessComponent.defaultProps = {
foo: "foo"
};
`,
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = {
bar: "bar"
};
`,
options: [{ allowRequiredDefaults: true }],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.propTypes.foo = Reblend.PropTypes.string;
MyStatelessComponent.defaultProps = {};
MyStatelessComponent.defaultProps.foo = "foo";
`,
},
{
code: `
function MyStatelessComponent({ foo }) {
return <div>{foo}</div>;
}
MyStatelessComponent.propTypes = {};
MyStatelessComponent.propTypes.foo = Reblend.PropTypes.string;
MyStatelessComponent.defaultProps = {};
MyStatelessComponent.defaultProps.foo = "foo";
`,
},
{
code: `
const types = {
foo: Reblend.PropTypes.string,
bar: Reblend.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: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = defaults;
`,
},
{
code: `
const defaults = {
foo: "foo"
};
const types = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
},
// createReblendClass components
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string.isRequired
}
});
`,
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: Reblend.PropTypes.string,
bar: Reblend.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: Reblend.PropTypes.string,
bar: Reblend.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>;
}
});
`,
},
// ES6 class component
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.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: Reblend.PropTypes.string.isRequired
};
Greeting.propTypes.foo = Reblend.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: Reblend.PropTypes.string.isRequired
};
Greeting.propTypes.foo = Reblend.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 = Reblend.PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
},
// edge cases
// not a reblend component
{
code: `
function NotAComponent({ foo, bar }) {}
NotAComponent.defaultProps = {
bar: "bar"
};
`,
},
{
code: `
class Greeting {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.defaulProps = {
bar: "bar"
};
`,
},
// external references
{
code: `
const defaults = require("./defaults");
const types = {
foo: Reblend.PropTypes.string,
bar: Reblend.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: Reblend.PropTypes.string
};
MyStatelessComponent.defaultProps = require("./defaults").foo;
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
MyStatelessComponent.propTypes = {
foo: Reblend.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: Reblend.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: Reblend.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: Reblend.PropTypes.string
};
MyStatelessComponent.defaultProps = {
foo: "foo"
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string
};
MyStatelessComponent.defaultProps = {
...defaults,
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
...someProps,
bar: Reblend.PropTypes.string.isRequired
};
`,
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.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: `
type PropsA = { foo?: string };
type PropsB = { bar?: string, fooBar: string };
type Props = PropsA & PropsB;
class Bar extends Reblend.Component {
props: Props;
static defaultProps = {
foo: "foo",
}
render() {
return <div>{this.props.foo} - {this.props.bar}</div>
}
}
`,
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'],
},
{
code: `
type DefaultProps1 = {|
bar1?: string
|};
type DefaultProps2 = {|
...DefaultProps1,
bar2?: string
|};
type Props = {
foo: string,
...DefaultProps2
};
function Hello(props: Props) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = {
bar1: "bar1",
bar2: "bar2",
};
`,
features: ['flow'],
},
{
code: `
type DefaultProps1 = {|
bar1?: string
|};
type DefaultProps2 = {|
...DefaultProps1,
bar2?: string
|};
type Props = {
foo: string,
...DefaultProps2
};
class Hello extends Reblend.Component<Props> {
render() {
return <div>Hello {props.foo}</div>;
}
}
Hello.defaultProps = {
bar1: "bar1",
bar2: "bar2",
};
`,
features: ['flow'],
},
// 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'],
},
// don't error when variable is not in scope with intersection
{
code: `
import type ImportedProps from "fake";
type Props = ImportedProps & {
foo: string
};
function Hello(props: Props) {
return <div>Hello {props.name.firstname}</div>;
}
`,
features: ['types'],
},
{
code: `
import type { FieldProps } from 'redux-form';
type Props = FieldProps & {
name: string,
type: string,
label?: string,
placeholder?: string,
disabled?: boolean,
};
TextField.defaultProps = {
label: '',
placeholder: '',
disabled: false,
};
`,
features: ['types'],
},
]),
invalid: parsers.all([
// stateless components
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = {
baz: "baz"
};
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 10,
column: 11,
},
],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = forbidExtraProps({
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
})
MyStatelessComponent.defaultProps = {
baz: "baz"
};
`,
settings: {
propWrapperFunctions: ['forbidExtraProps'],
},
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 10,
column: 11,
},
],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
const propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.propTypes = forbidExtraProps(propTypes);
MyStatelessComponent.defaultProps = {
baz: "baz"
};
`,
settings: {
propWrapperFunctions: ['forbidExtraProps'],
},
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 11,
column: 11,
},
],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = {
baz: "baz"
};
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 10,
column: 11,
},
],
options: [{ allowRequiredDefaults: true }],
},
{
code: `
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
MyStatelessComponent.defaultProps = {
bar: "bar"
};
MyStatelessComponent.defaultProps.baz = "baz";
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'bar' },
line: 10,
column: 11,
},
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 12,
column: 9,
},
],
},
{
code: `
const types = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = {
bar: "bar"
};
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'bar' },
line: 12,
column: 11,
},
],
},
{
code: `
const defaults = {
foo: "foo"
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string
};
MyStatelessComponent.defaultProps = defaults;
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 3,
column: 11,
},
],
},
{
code: `
const defaults = {
foo: "foo"
};
const types = {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string
};
function MyStatelessComponent({ foo, bar }) {
return <div>{foo}{bar}</div>;
}
MyStatelessComponent.propTypes = types;
MyStatelessComponent.defaultProps = defaults;
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 3,
column: 11,
},
],
},
// createReblendClass components
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
},
getDefaultProps: function() {
return {
baz: "baz"
};
}
});
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 12,
column: 15,
},
],
},
{
code: `
var Greeting = createReblendClass({
render: function() {
return <div>Hello {this.props.foo} {this.props.bar}</div>;
},
propTypes: {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string
},
getDefaultProps: function() {
return {
foo: "foo"
};
}
});
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 12,
column: 15,
},
],
},
// ES6 class component
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
Greeting.defaultProps = {
baz: "baz"
};
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 14,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
foo: Reblend.PropTypes.string.isRequired,
bar: Reblend.PropTypes.string
};
Greeting.defaultProps = {
foo: "foo"
};
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 14,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: Reblend.PropTypes.string.isRequired
};
Greeting.propTypes.foo = Reblend.PropTypes.string.isRequired;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 14,
column: 9,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {
bar: Reblend.PropTypes.string
};
Greeting.propTypes.foo = Reblend.PropTypes.string;
Greeting.defaultProps = {};
Greeting.defaultProps.baz = "baz";
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 14,
column: 9,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
Greeting.propTypes = {};
Greeting.propTypes.foo = Reblend.PropTypes.string.isRequired;
Greeting.defaultProps = {};
Greeting.defaultProps.foo = "foo";
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 12,
column: 9,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
const props = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
Greeting.propTypes = props;
const defaults = {
bar: "bar"
};
Greeting.defaultProps = defaults;
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'bar' },
line: 15,
column: 11,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
}
const props = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string
};
const defaults = {
baz: "baz"
};
Greeting.propTypes = props;
Greeting.defaultProps = defaults;
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 14,
column: 11,
},
],
},
// ES6 classes with static getter methods
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
name: Reblend.PropTypes.string.isRequired
};
}
static get defaultProps() {
return {
name: "name"
};
}
render() {
return <div>Hello {this.props.name}</div>;
}
}
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'name' },
line: 10,
column: 15,
},
],
},
{
code: `
class Hello extends Reblend.Component {
static get propTypes() {
return {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string
};
}
static get defaultProps() {
return {
baz: "world"
};
}
render() {
return <div>Hello {this.props.bar}</div>;
}
}
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 11,
column: 15,
},
],
},
{
code: `
const props = {
foo: Reblend.PropTypes.string
};
const defaults = {
baz: "baz"
};
class Hello extends Reblend.Component {
static get propTypes() {
return props;
}
static get defaultProps() {
return defaults;
}
render() {
return <div>Hello {this.props.foo}</div>;
}
}
`,
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 6,
column: 11,
},
],
},
{
code: `
const defaults = {
bar: "world"
};
class Hello extends Reblend.Component {
static get propTypes() {
return {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
}
static get defaultProps() {
return defaults;
}
render() {
return <div>Hello {this.props.bar}</div>;
}
}
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'bar' },
line: 3,
column: 11,
},
],
},
// 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: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
static defaultProps = {
bar: "bar"
};
}
`,
features: ['class fields'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'bar' },
line: 13,
column: 13,
},
],
},
{
code: `
class Greeting extends Reblend.Component {
render() {
return (
<h1>Hello, {this.props.foo} {this.props.bar}</h1>
);
}
static propTypes = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string
};
static defaultProps = {
baz: "baz"
};
}
`,
features: ['class fields'],
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'baz' },
line: 13,
column: 13,
},
],
},
{
code: `
const props = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string.isRequired
};
const defaults = {
bar: "bar"
};
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: 'requiredHasDefault',
data: { name: 'bar' },
line: 7,
column: 11,
},
],
},
{
code: `
const props = {
foo: Reblend.PropTypes.string,
bar: Reblend.PropTypes.string
};
const defaults = {
baz: "baz"
};
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: 'defaultHasNoType',
data: { name: 'baz' },
line: 7,
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: Reblend.PropTypes.string.isRequired
};
Greetings.Hello.defaultProps = {
foo: "foo"
};
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 12,
column: 11,
},
],
},
{
code: `
var Greetings = ({ foo = "foo" }) => {
return <div>Hello {this.props.foo}</div>;
}
Greetings.propTypes = {
foo: Reblend.PropTypes.string.isRequired
};
Greetings.defaultProps = {
foo: "foo"
};
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 9,
column: 11,
},
],
},
// with Flow annotations
{
code: `
class Hello extends Reblend.Component {
props: {
foo: string,
bar?: string
};
render() {
return <div>Hello {this.props.foo}</div>;
}
}
Hello.defaultProps = {
foo: "foo"
};
`,
features: ['types'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 14,
column: 11,
},
],
},
{
code: `
function Hello(props: { foo: string }) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = {
foo: "foo"
}
`,
features: ['types'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 6,
column: 11,
},
],
},
{
code: `
type Props = {
foo: string
};
function Hello(props: Props) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = {
foo: "foo"
}
`,
features: ['types'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 10,
column: 11,
},
],
},
{
code: `
const Hello = (props: { foo: string, bar?: string }) => {
return <div>Hello {props.foo}</div>;
};
Hello.defaultProps = { foo: "foo", bar: "bar" };
`,
features: ['types'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 5,
column: 32,
},
],
},
{
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 = { foo: "foo", frob: "frob" };
`,
features: ['flow'], // TODO: FIXME: change to "types" and fix failures
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 15,
column: 32,
},
{
messageId: 'defaultHasNoType',
data: { name: 'frob' },
line: 15,
column: 44,
},
],
},
{
code: `
type PropsA = { foo: string };
type PropsB = { bar: string };
type Props = PropsA & PropsB;
class Bar extends Reblend.Component {
props: Props;
static defaultProps = {
fooBar: "fooBar",
foo: "foo",
}
render() {
return <div>{this.props.foo} - {this.props.bar}</div>
}
}
`,
features: ['types'],
errors: [
{
messageId: 'defaultHasNoType',
data: { name: 'fooBar' },
},
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
},
],
},
{
code: `
class SomeComponent extends Reblend.Component {
render() {
return <div />;
}
}
SomeComponent.propTypes = {
"firstProperty": PropTypes.string.isRequired,
};
SomeComponent.defaultProps = {
"firstProperty": () => undefined
};
`,
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'firstProperty' },
},
],
},
{
code: `
type DefaultProps = {
baz?: string,
bar?: string
};
type Props = {
foo: string,
...DefaultProps
}
function Hello(props: Props) {
return <div>Hello {props.foo}</div>;
}
Hello.defaultProps = { foo: "foo", frob: "frob", baz: "bar" };
`,
features: ['flow'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 15,
column: 32,
},
{
messageId: 'defaultHasNoType',
data: { name: 'frob' },
line: 15,
column: 44,
},
],
},
{
code: `
type DefaultProps = {
baz?: string,
bar?: string
};
type Props = {
foo: string,
...DefaultProps
}
class Hello extends Reblend.Component<Props> {
render() {
return <div>Hello {props.foo}</div>;
}
}
Hello.defaultProps = { foo: "foo", frob: "frob", baz: "bar" };
`,
features: ['flow'],
errors: [
{
messageId: 'requiredHasDefault',
data: { name: 'foo' },
line: 17,
column: 32,
},
{
messageId: 'defaultHasNoType',
data: { name: 'frob' },
line: 17,
column: 44,
},
],
},
{
code: `
export type SharedProps = {|
disabled: boolean,
|};
type Props = {|
...SharedProps,
focused?: boolean,
|};
class Foo extends Reblend.Component<Props> {
static defaultProps = {
disabled: false
};
};
`,
features: ['flow'],
errors: [
{
messageId: 'requiredHasDefault',
data: {
name: 'disabled',
},
},
],
},
]),
});