This commit is contained in:
qingzhou 2018-01-16 15:44:00 -08:00 коммит произвёл Cătălin Mariș
Родитель e85f68838d
Коммит fbeaf8dce2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 37400B4AAAC45DC1
7 изменённых файлов: 10628 добавлений и 0 удалений

7
.npmrc Normal file
Просмотреть файл

@ -0,0 +1,7 @@
@mskeros:registry=https://mseng.pkgs.visualstudio.com/_packaging/Keros/npm/registry/
always-auth=true
; Treat this auth token like a password. Do not share it with anyone, including Microsoft support. This token expires on or before 3/13/2018.
; begin auth token
//mseng.pkgs.visualstudio.com/_packaging/Keros/npm/registry/:_authToken=${AUTH_TOKEN}
//mseng.pkgs.visualstudio.com/_packaging/Keros/npm/:_authToken=${AUTH_TOKEN}
; end auth token

13
rules/chisel/.sonarwhalrc Normal file
Просмотреть файл

@ -0,0 +1,13 @@
{
"connector": {
"name": "chrome",
"options": {
"waitFor": 1000
}
},
"formatters": "stylish",
"rulesTimeout": 120000,
"rules": {
"chisel": "warning"
}
}

38
rules/chisel/README.md Normal file
Просмотреть файл

@ -0,0 +1,38 @@
# chisel (`chisel`)
Use Chisel to assess the accessibility of a web site or web application.
## Can the rule be configured?
Yes, you can configure this rule in your `sonarwhal` configuration file as
follows:
```json
"rules": {
"chisel": ["warning", { "testsToRun": "color-contrast"}]
}
```
The available parameters include:
* `testsToRun?`: `string[]` - (Optional) Used to set the rules that will be run
in chisel test run.
* `dom?`: `NodeSelector & Node | NodeList` - (Optional) Used to set the elements
that will be tested; defaults to document if dom, selector, and
include/exclude are not set.
* `selector?`: `string` - (Optional) Uses selector to get the elements that will
be tested; ignored if dom is set.
* `include?`: `string[]` - (Optional) Uses an array of selectors to get the
elements that will be tested and can be used with exclude; ignored if dom or
selector are set.
* `exclude?`: `string[]` - (Optional) Uses an array of selectors to get the
elements that will not be tested and can be used with include; ignored if
dom or selector are set.
* `enableBestPracticeRules?`: `boolean` - (Optional, default is `false`) Enables
rule that are not mapped to MAS/considered best practice; default is false
and ignored if testsToRun is set.

10264
rules/chisel/package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

67
rules/chisel/package.json Normal file
Просмотреть файл

@ -0,0 +1,67 @@
{
"author": "",
"ava": {
"babel": {
"presets": []
},
"concurrency": 5,
"failFast": false,
"files": [
"dist/tests/**/*.js"
],
"timeout": "1m"
},
"description": "Use Chisel to assess the accessibility of web sites and web applications",
"devDependencies": {
"@types/debug": "0.0.30",
"@types/node": "8.0.14",
"async-retry": "^1.1.4",
"ava": "^0.24.0",
"babel-cli": "^6.26.0",
"babel-plugin-istanbul": "^4.1.5",
"babel-preset-es2017": "^6.22.0",
"babel-register": "^6.26.0",
"cpx": "^1.5.0",
"eslint": "^4.15.0",
"eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-typescript": "^0.8.1",
"express": "^4.16.2",
"markdownlint-cli": "^0.6.0",
"npm-run-all": "^4.1.2",
"on-headers": "^1.0.1",
"rimraf": "^2.6.2",
"sonarwhal": "^0.22.1",
"typescript": "^2.6.2",
"typescript-eslint-parser": "^12.0.0"
},
"engines": {
"node": ">=8.0.0"
},
"keywords": [
"rule",
"sonarwhal"
],
"main": "dist/src/index.js",
"name": "@sonarwhal/rule-chisel",
"scripts": {
"build": "npm run clean && npm-run-all build:*",
"build:assets": "cpx \"./{src,tests}/**/{!(*.ts),.!(ts)}\" dist",
"build:ts": "tsc --outDir dist --rootDir .",
"clean": "rimraf dist",
"test": "npm run lint && npm run build && ava",
"lint": "npm-run-all lint:*",
"lint:js": "eslint --ext md --ext ts --ignore-pattern dist .",
"lint:md": "markdownlint README.md",
"watch:ts": "npm run build:ts -- --watch",
"init": "npm install && npm run build",
"sonarwhal": "node node_modules/sonarwhal/dist/src/bin/sonarwhal.js"
},
"peerDependencies": {
"sonarwhal": "^0.22.1"
},
"dependencies": {
"@mskeros/chisel": "^1.127.2"
},
"repository": "https://github.com/Microsoft/sonarwhal-microsoft-rules",
"version": "0.1.0"
}

176
rules/chisel/src/index.ts Normal file
Просмотреть файл

@ -0,0 +1,176 @@
/**
* @fileoverview Use Chisel to assess the accessibility of a web site or web application.
*/
import { Category } from 'sonarwhal/dist/src/lib/enums/category';
import { RuleContext } from 'sonarwhal/dist/src/lib/rule-context';
import { IRule, IRuleBuilder, ITraverseEnd, IAsyncHTMLElement, Severity } from 'sonarwhal/dist/src/lib/types';
import { debug as d } from 'sonarwhal/dist/src/lib/utils/debug';
import { IChiselResults, AxeNodeResult, AxeRule, IChiselDecorations, IChiselOptions } from '@mskeros/chisel';
import { readFileAsync } from 'sonarwhal/dist/src/lib/utils/misc';
const debug: debug.IDebugger = d(__filename);
/*
* ------------------------------------------------------------------------------
* Public
* ------------------------------------------------------------------------------
*/
const rule: IRuleBuilder = {
create(context: RuleContext): IRule {
/** Configuration of the chisel rule. */
let chiselConfig: IChiselOptions = {};
/** Table of overriden config properties. */
const overrideLookUp: object = {
dom: ['include', 'exclude'],
selector: ['include', 'exclude'],
testsToRun: ['enableBestPracticeRules']
};
/** Interface of a chisel violation. */
interface IChiselViolation extends AxeRule, IChiselDecorations { }
/** Load rule configuration. */
const loadRuleConfig = () => {
if (!context.ruleOptions) {
return;
}
chiselConfig = context.ruleOptions;
};
/** Generate the script to initiate the chisel scan in a page. */
const generateScript = (): string => {
// This is run in the page, not sonarwhal itself.
const script: string =
`function runChiselScan() {
return new Promise(function (resolve, reject) {
Chisel.scan(${JSON.stringify(chiselConfig, null, 2)}, function (results) {
resolve(results);
}, function (error) {
reject(error);
});
});
}`;
return script;
};
/** Get element from an axe node in the scan result. */
const getElement = async (node: AxeNodeResult): Promise<IAsyncHTMLElement> => {
const selector: string = node.target[0];
const elements: Array<IAsyncHTMLElement> = await context.querySelectorAll(selector);
return elements[0];
};
/** Validate config to warn against overriden properties. */
const validateConfig = (config: IChiselOptions) => {
const warnings: Array<string> = [];
Object.keys(overrideLookUp).forEach((dominant: string) => {
if (config.hasOwnProperty(dominant)) {
const lessers = overrideLookUp[dominant].filter((lesserProperty) => {
return config.hasOwnProperty(lesserProperty);
});
if (lessers.length) {
warnings.push(`${lessers.join(', ')} is ignored when ${dominant} is set.`);
}
}
});
return warnings;
};
const validate = async (traveseEnd: ITraverseEnd) => {
const { resource } = traveseEnd;
const chiselScript: string = await readFileAsync(require.resolve('@mskeros/chisel'));
const runChiselScript: string = `(function() {
${chiselScript};
return ${generateScript()}();
}())`;
let result: IChiselResults = null;
const warnings = validateConfig(chiselConfig);
if (warnings.length) {
const reportPromises = warnings.map((warning) => {
return context.report(resource, null, warning, null, null, Severity.warning);
});
await Promise.all(reportPromises);
}
try {
result = await context.evaluate(runChiselScript);
} catch (error) {
await context.report(resource, null, `Error executing script${error.message ? `: "${error.message}"` : ''}. Please try with another connector`, null, null, Severity.warning);
debug('Error executing script %O', error);
return;
}
if (!result || !Array.isArray(result.violations)) {
debug(`Unable to parse chisel results ${result}`);
return;
}
if (result.violations.length === 0) {
debug('No accessibility issues found');
return;
}
const reportPromises = result.violations.reduce((promises: Array<Promise<void>>, violation: IChiselViolation) => {
const elementPromises = violation.nodes.map(async (node: AxeNodeResult) => {
const element: IAsyncHTMLElement = await getElement(node);
return context.report(resource, element, violation.help);
});
return promises.concat(elementPromises);
}, []);
await Promise.all(reportPromises);
};
loadRuleConfig();
return { 'traverse::end': validate };
},
meta: {
docs: {
category: Category.accessibility,
description: `Use Chisel to assess the accessibility of a web site or web application.`
},
recommended: false,
schema: [{
additionalProperties: false,
properties: {
dom: { type: ['string', 'array'] },
enableBestPracticeRules: { type: 'boolean' },
exclude: {
items: { type: 'string' },
type: 'array'
},
include: {
items: { type: 'string' },
type: 'array'
},
selector: { type: 'string' },
testsToRun: {
items: { type: 'string' },
type: 'array'
}
}
}],
worksWithLocalFiles: false
}
};
module.exports = { chisel: rule };

Просмотреть файл

@ -0,0 +1,63 @@
import { generateHTMLPage } from 'sonarwhal/dist/tests/helpers/misc';
import { getRuleName } from 'sonarwhal/dist/src/lib/utils/rule-helpers';
import { IRuleTest } from 'sonarwhal/dist/tests/helpers/rule-test-type';
import * as ruleRunner from 'sonarwhal/dist/tests/helpers/rule-runner';
const ruleName = getRuleName(__dirname);
const html = {
missingLang: `<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
</body>
</html>`,
noProblems: generateHTMLPage()
};
const tests: Array<IRuleTest> = [
{
name: `Page doesn't have any a11y problems and passes`,
serverConfig: html.noProblems
},
{
name: `HTML is missing the lang attribute and fails`,
reports: [{ message: '<html> element must have a lang attribute' }],
serverConfig: html.missingLang
}
];
const testsWithCustomConfiguration: Array<IRuleTest> = [
{
name: `Page doesn't have any a11y problems and passes`,
serverConfig: html.noProblems
},
{
name: `HTML is missing the lang attribute and passes because of custom config`,
serverConfig: html.missingLang
}
];
const testsWithOverridenConfiguration: Array<IRuleTest> = [
{
name: `Page doesn't have any a11y problems and passes, but warns about the overriden config setting`,
reports: [{ message: 'exclude is ignored when selector is set.' }],
serverConfig: html.noProblems
},
{
name: `HTML is missing the lang attribute and fails, and warns about the overriden config setting`,
reports: [{ message: 'exclude is ignored when selector is set.' }, { message: '<html> element must have a lang attribute' }],
serverConfig: html.missingLang
}
];
ruleRunner.testRule(ruleName, tests);
ruleRunner.testRule(ruleName, testsWithCustomConfiguration, { ruleOptions: { testsToRun: ['color-contrast'] } });
ruleRunner.testRule(ruleName, testsWithOverridenConfiguration, {
ruleOptions: {
exclude: ['html'],
selector: 'html'
}
});