Skip to content

Commit

Permalink
1. Updated eslint and plugin versions
Browse files Browse the repository at this point in the history
2. Added the AirBnB eslint config to inherit
3. Fixed all linter findings
  • Loading branch information
Erin Doyle committed Apr 9, 2017
1 parent 36589bf commit d0401be
Show file tree
Hide file tree
Showing 37 changed files with 1,503 additions and 1,801 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lib/
webpack.config.js
417 changes: 52 additions & 365 deletions .eslintrc

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions .jshintignore

This file was deleted.

5 changes: 0 additions & 5 deletions .jshintrc

This file was deleted.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,19 @@
"license": "MIT",
"devDependencies": {
"babel-cli": "^6.6.5",
"babel-eslint": "^6.0.2",
"babel-loader": "^6.2.4",
"babel-plugin-transform-runtime": "^6.7.5",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"babel-runtime": "^5.8.38",
"chai": "^3.5.0",
"eslint": "^2.7.0",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-plugin-babel": "^3.1.0",
"eslint-plugin-react": "^4.2.3",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^3.0.2",
"eslint-plugin-react": "^6.10.3",
"karma": "^0.13.22",
"karma-chrome-launcher": "^0.1.7",
"karma-cli": "^0.1.2",
Expand Down
236 changes: 116 additions & 120 deletions src/a11y.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import after from './after'
import validate from './options'
import browser from './util/browser'
import Suite from './test'
import after from './after';
import validate from './options';
import browser from './util/browser';
import Suite from './test';

export default class A11y {

Expand All @@ -11,140 +11,136 @@ export default class A11y {
* @arg {object} options - the options
* @returns {A11y} The react-a11y instance
*/
constructor (...args) {
const {
constructor(...args) {
const {
React
, ReactDOM
, ...options
} = validate(...args)
} = validate(...args);

this.options = options
this.React = React
this.ReactDOM = ReactDOM
this.suite = new Suite(React, ReactDOM, this.options)
this.patchReact()
}
this.options = options;
this.React = React;
this.ReactDOM = ReactDOM;
this.suite = new Suite(React, ReactDOM, this.options);
this.patchReact();
}

/**
* Patch React, replacing its createElement by our implementation
* so we can run the tests
* @returns {undefined}
*/
patchReact () {

// save old createElement
this._createElement = this.React.createElement

const that = this
this.React.createElement = function (klass, _props = {}, ...children) {

// fix for props = null
const props = _props || {}

// create a refs object to hold the ref.
// this needs to be an object so that it can be passed
// by reference, and hold chaning state.
const refs = typeof props.ref === 'string' ? props.ref : {}
const ref = typeof props.ref === 'string'
? props.ref
: function (node) {
refs.node = node

// maintain behaviour when ref prop was already set
if ( typeof props.ref === 'function' ) {
props.ref(node)
}
}

const newProps = typeof klass === 'string' ? { ...props, ref } : props

const reactEl = that._createElement(klass, newProps, ...children)

// only test html elements
if (typeof klass === 'string') {

const handler = that.failureHandler(reactEl, refs)
const childrenForTest = children.length === 0
? props.children || []
: children

that.suite.test(klass, props, childrenForTest, handler)
}

return reactEl
patchReact() {
// save old createElement
this._createElement = this.React.createElement;

const that = this;
this.React.createElement = function (klass, _props = {}, ...children) {
// fix for props = null
const props = _props || {};

// create a refs object to hold the ref.
// this needs to be an object so that it can be passed
// by reference, and hold chaning state.
const refs = typeof props.ref === 'string' ? props.ref : {};
const ref = typeof props.ref === 'string'
? props.ref
: (node) => {
refs.node = node;

// maintain behaviour when ref prop was already set
if (typeof props.ref === 'function') {
props.ref(node);
}
};

const newProps = typeof klass === 'string' ? { ...props, ref } : props;

const reactEl = that._createElement(klass, newProps, ...children);

// only test html elements
if (typeof klass === 'string') {
const handler = that.failureHandler(reactEl, refs);
const childrenForTest = children.length === 0
? props.children || []
: children;

that.suite.test(klass, props, childrenForTest, handler);
}

return reactEl;
};
}
}

/**
* Restore React and all components as if we were never here
* @returns {undefined}
/**
* Restore React and all components as if we were never here
* @returns {undefined}
*/
restoreAll () {
this.React.createElement = this._createElement
after.restorePatchedMethods()
}
restoreAll() {
this.React.createElement = this._createElement;
after.restorePatchedMethods();
}

/**
* Creates a failure handler based on the element that was created
* @arg {object} reactEl - The react element this failure is for
* @arg {object} ref - The object that holds the DOM node (passed by ref)
* @returns {function} A handler that knows everything it needs to know
*/
failureHandler (reactEl, ref) {
const {
reporter
, filterFn
} = this.options

/**
* @arg {string} errInfo - All the error info (see docs what this means)
* @returns {undefined}
*/
return function (errInfo) {

// get the owning component (the one that has
// the element in its render fn)
const owner = reactEl._owner

// if there is an owner, use its name
// if not, use the tagname of the violating elemnent
const displayName = owner && owner.getName()

// stop if we're not allowed to proceed
if ( !filterFn(displayName, errInfo.props.id, errInfo.msg) ) {
return
}

// gather all info for the reporter
const info = {
...errInfo
, displayName
}

// if we need to include the rendered node, we need to wait until
// the owner has rendered
// TODO: reduce the number of case where ther is no instance
// by forcing every component to have one.
if ( browser && !this.__sync && owner && owner._instance ) {
const instance = owner._instance
// Cannot log a node reference until the component is in the DOM,
// so defer the call until componentDidMount or componentDidUpdate.
after.render(instance, function () {
// unpack the ref
let DOMNode = false
if ( typeof ref === 'string' ) {
DOMNode = this.ReactDOM.findDOMNode(instance.refs[ref])
} else if ( 'node' in ref ) {
DOMNode = ref.node
} else {
throw new Error('could not resolve ref')
}

reporter({ ...info, DOMNode })
}.bind(this))
} else {
reporter(info)
}
}.bind(this)
}
failureHandler(reactEl, ref) {
const {
reporter,
filterFn
} = this.options;

/**
* @arg {string} errInfo - All the error info (see docs what this means)
* @returns {undefined}
*/
return function (errInfo) {
// get the owning component (the one that has
// the element in its render fn)
const owner = reactEl._owner;

// if there is an owner, use its name
// if not, use the tagname of the violating elemnent
const displayName = owner && owner.getName();

// stop if we're not allowed to proceed
if (!filterFn(displayName, errInfo.props.id, errInfo.msg)) {
return;
}

// gather all info for the reporter
const info = {
...errInfo,
displayName
};

// if we need to include the rendered node, we need to wait until
// the owner has rendered
// TODO: reduce the number of case where ther is no instance
// by forcing every component to have one.
if (browser && !this.__sync && owner && owner._instance) {
const instance = owner._instance;
// Cannot log a node reference until the component is in the DOM,
// so defer the call until componentDidMount or componentDidUpdate.
after.render(instance, () => {
// unpack the ref
let DOMNode = false;
if (typeof ref === 'string') {
DOMNode = this.ReactDOM.findDOMNode(instance.refs[ref]); // TODO: replace use of findDOMNode
} else if ('node' in ref) {
DOMNode = ref.node;
} else {
throw new Error('could not resolve ref');
}

reporter({ ...info, DOMNode });
});
} else {
reporter(info);
}
}.bind(this);
}
}
58 changes: 29 additions & 29 deletions src/after.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@

// store how to undo these changes
let restoreFunctions = []
let restoreFunctions = [];

// does nothing
const noop = () => null
const noop = () => null;

const after = function (host, name, cb) {
if ( !host ) {
throw new Error('cannot replace function on undefined')
}
const after = (host, name, cb) => {
if (!host) {
throw new Error('cannot replace function on undefined');
}

// save original
const original = host[name] || noop
// save original
const original = host[name] || noop;

// override host
host[name] = function (...args) {
// override host
host[name] = (...args) => {
// perform original
original.apply(this, args)
original.apply(this, args);
// perform cb
cb.apply(this, args)
}
cb.apply(this, args);
};

// save restoring function
restoreFunctions.push(function () {
host[name] = original
})
}
// save restoring function
restoreFunctions.push(() => {
host[name] = original;
});
};

after.restorePatchedMethods = function () {
// perform each restoring function
restoreFunctions.forEach(restore => restore())
after.restorePatchedMethods = () => {
// perform each restoring function
restoreFunctions.forEach(restore => restore());

// clear the list of functions to restore
restoreFunctions = []
}
// clear the list of functions to restore
restoreFunctions = [];
};

after.render = function (component, fn) {
after(component, 'componentDidMount', fn)
after(component, 'componentDidUpdate', fn)
}
after.render = (component, fn) => {
after(component, 'componentDidMount', fn);
after(component, 'componentDidUpdate', fn);
};

export default after
export default after;
Loading

0 comments on commit d0401be

Please sign in to comment.