Skip to content

Commit

Permalink
[Added] Ability to exclude tests (resolves #57)
Browse files Browse the repository at this point in the history
  • Loading branch information
Todd Kloots committed Jun 10, 2015
1 parent 74232d1 commit 6f38fe8
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 27 deletions.
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = function (config) {
devtool: 'inline-source-map',
module: {
loaders: [
{ test: /\.js$/, loader: 'babel-loader' }
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime' }
]
},
plugins: [
Expand Down
24 changes: 22 additions & 2 deletions lib/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe('tags', () => {
});

it('dissallows the word "image" in the alt attribute', () => {
expectWarning(assertions.tags.img.REDUDANT_ALT.msg, () => {
expectWarning(assertions.tags.img.REDUNDANT_ALT.msg, () => {
<img src="cat.gif" alt="image of a cat"/>;
});
});
Expand Down Expand Up @@ -524,4 +524,24 @@ describe('device is set to mobile', () => {
});
});
});
});
});

describe('exclusions', () => {
var createElement = React.createElement;

before(() => {
a11y(React, { exclude: ['REDUNDANT_ALT'] });
});

after(() => {
React.createElement = createElement;
});

describe('when REDUNDANT_ALT is excluded', () => {
it('does not warn when the word "image" in the alt attribute', () => {
doNotExpectWarning(assertions.tags.img.REDUNDANT_ALT.msg, () => {
<img src="cat.gif" alt="image of a cat"/>;
});
});
});
});
7 changes: 1 addition & 6 deletions lib/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ var INTERACTIVE = {
}
};

const DEVICE = { 'DESKTOP': 'desktop', 'MOBILE': 'mobile' };

var hasAlt = (props) => {
return typeof props.alt === 'string';
};
Expand Down Expand Up @@ -117,7 +115,6 @@ exports.props = {
},

NO_TABINDEX: {
device: DEVICE.DESKTOP,
msg: 'You have a click handler on a non-interactive element but no `tabIndex` DOM property. The element will not be navigable or interactive by keyboard users. http://www.w3.org/TR/wai-aria-practices/#focus_tabindex',
test (tagName, props, children) {
return !(
Expand All @@ -128,15 +125,13 @@ exports.props = {
},

BUTTON_ROLE_SPACE: {
device: DEVICE.DESKTOP,
msg: 'You have `role="button"` but did not define an `onKeyDown` handler. Add it, and have the "Space" key do the same thing as an `onClick` handler.',
test (tagName, props, children) {
return !(props.role === 'button' && !props.onKeyDown);
}
},

BUTTON_ROLE_ENTER: {
device: DEVICE.DESKTOP,
msg: 'You have `role="button"` but did not define an `onKeyDown` handler. Add it, and have the "Enter" key do the same thing as an `onClick` handler.',
test (tagName, props, children) {
return !(props.role === 'button' && !props.onKeyDown);
Expand Down Expand Up @@ -170,7 +165,7 @@ exports.tags = {
}
},

REDUDANT_ALT: {
REDUNDANT_ALT: {
// TODO: have some way to set localization strings to match against
msg: 'Screen-readers already announce `img` tags as an image, you don\'t need to use the word "image" in the description',
test (tagName, props, children) {
Expand Down
40 changes: 24 additions & 16 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
var assertions = require('./assertions');
var after = require('./after');

var deviceMatches = (test, deviceFilter) => {
if (!test.device)
return true;
const mobileExclusions = [
'NO_TABINDEX',
'BUTTON_ROLE_SPACE',
'BUTTON_ROLE_ENTER'
];

var shouldRunTest = (testName, options) => {
var exclude = options.exclude || [];

if (options.device == 'mobile') {
exclude = new Set(exclude.concat(mobileExclusions));
exclude = [...exclude];
}

return (deviceFilter.indexOf(test.device) != -1);
return (exclude.indexOf(testName) == -1);
};

var runTagTests = (tagName, props, children, deviceFilter, onFailure) => {
var runTagTests = (tagName, props, children, options, onFailure) => {
var key;
var tagTests = assertions.tags[tagName] || [];

for (key in tagTests) {
let shouldRunTest = deviceMatches(tagTests[key], deviceFilter);
let testFailed = shouldRunTest &&
let testFailed = shouldRunTest(key, options) &&
!tagTests[key].test(tagName, props, children);

if (tagTests[key] && testFailed)
onFailure(tagName, props, tagTests[key].msg);
}
};

var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
var runPropTests = (tagName, props, children, options, onFailure) => {
var key;
var propTests;

Expand All @@ -32,8 +41,7 @@ var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
propTests = assertions.props[propName] || [];

for (key in propTests) {
let shouldRunTest = deviceMatches(propTests[key], deviceFilter);
let testTailed = shouldRunTest &&
let testTailed = shouldRunTest(key, options) &&
!propTests[key].test(tagName, props, children);

if (propTests[key] && testTailed)
Expand All @@ -42,12 +50,12 @@ var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
}
};

var runLabelTests = (tagName, props, children, deviceFilter, onFailure) => {
var runLabelTests = (tagName, props, children, options, onFailure) => {
var key;
var renderTests = assertions.render;

for (key in renderTests) {
if (renderTests[key]) {
if (shouldRunTest(key, options) && renderTests[key]) {
let failureCB = onFailure.bind(
undefined, tagName, props, renderTests[key].msg);

Expand All @@ -56,10 +64,10 @@ var runLabelTests = (tagName, props, children, deviceFilter, onFailure) => {
}
};

var runTests = (tagName, props, children, deviceFilter, onFailure) => {
var runTests = (tagName, props, children, options, onFailure) => {
var tests = [runTagTests, runPropTests, runLabelTests];
tests.map((test) => {
test(tagName, props, children, deviceFilter, onFailure);
test(tagName, props, children, options, onFailure);
});
};

Expand Down Expand Up @@ -168,17 +176,17 @@ var reactA11y = (React, options) => {
assertions.setReact(React);

_createElement = React.createElement;
var deviceFilter = options && options.device || ['desktop'];

React.createElement = (type, _props, ...children) => {
var props = _props || {};
options = options || {};

props.id = createId(props);
var reactEl = _createElement.apply(this, [type, props].concat(children));
var failureCB = handleFailure.bind(undefined, options, reactEl);

if (typeof type === 'string')
runTests(type, props, children, deviceFilter, failureCB);
runTests(type, props, children, options, failureCB);

return reactEl;
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"test": "jsxhint . && karma start --single-run",
"watch-tests": "npm test -- --watch",
"prepublish": "babel -d dist lib",
"prepublish": "babel --optional runtime lib --out-dir dist",
"release": "release"
},
"authors": [
Expand All @@ -25,6 +25,7 @@
"babel": "^5.2.17",
"babel-core": "^5.2.17",
"babel-loader": "^5.0.0",
"babel-runtime": "^5.1.11",
"jsx-loader": "^0.12.2",
"jsxhint": "^0.8.1",
"karma": "^0.12.28",
Expand Down
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ module.exports = {

module: {
loaders: [
{ test: /\.js$/, loader: 'jsx-loader?harmony' }
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime' }
]
}

Expand Down

0 comments on commit 6f38fe8

Please sign in to comment.