-
Notifications
You must be signed in to change notification settings - Fork 78
chore: Generate and display implementation metrics #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7324b09
b820fda
460a548
ee046e7
ea6bd90
d9cf79a
ac4a7b6
0e24f94
1587ffa
9d2410d
f06c716
13b6300
559861e
5d0b583
87441f9
655fa19
dc173f4
2c9b154
91a2bc3
e1a6fc1
71d329f
e1a5a26
11a6a13
ed68a9e
2e10dea
276937d
1721428
f5a9388
9452f0b
d748044
47dd52f
c5a3548
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/** | ||
* Create glossary usages | ||
* -> for each glossary item (find references in each rule) | ||
* -> this is saved in `_data` which is later used in `pages/glossary` | ||
*/ | ||
const globby = require('globby') | ||
const regexps = require('../utils/reg-exps') | ||
const createFile = require('../utils/create-file') | ||
const getAllMatchesForRegex = require('../utils/get-all-matches-for-regex') | ||
const getMarkdownData = require('../utils/get-markdown-data') | ||
|
||
const init = async () => { | ||
/** | ||
* Get all rules `markdown` data | ||
*/ | ||
const rulesData = globby | ||
.sync([`./_rules/*.md`]) | ||
.map(rulePath => getMarkdownData(rulePath)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have basically the same code sitting in |
||
|
||
/** | ||
* Eg: | ||
* { | ||
* `non-empty`: [ | ||
* { name: `aria valid ...`, slug: `rules/XXXXX` }, | ||
* .... | ||
* ] | ||
* .... | ||
* } | ||
*/ | ||
const glossaryUsages = {} | ||
|
||
rulesData.forEach(ruleData => { | ||
const { frontmatter, body } = ruleData | ||
const { | ||
id: ruleId, | ||
name: ruleName, | ||
accessibility_requirements: ruleAccessibilityRequirements, | ||
} = frontmatter | ||
|
||
const glossaryMatches = getAllMatchesForRegex( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea what is happening here. Can you please write this it can be reviewed? |
||
regexps.glossaryReferenceInRules, | ||
body, | ||
false | ||
) | ||
|
||
glossaryMatches.forEach(glossaryItem => { | ||
const hasGlossaryKey = regexps.glossaryKey.test(glossaryItem.block) | ||
if (!hasGlossaryKey) { | ||
return | ||
} | ||
|
||
const key = glossaryItem.block.match(regexps.glossaryKey)[1] | ||
if (!key) { | ||
return | ||
} | ||
|
||
const usage = { | ||
name: ruleName, | ||
slug: `rules/${ruleId}`, | ||
} | ||
if (!glossaryUsages[key]) { | ||
glossaryUsages[key] = [usage] | ||
return | ||
} | ||
|
||
const exists = glossaryUsages[key].some(u => u.slug === usage.slug) | ||
if (exists) { | ||
return | ||
} | ||
|
||
glossaryUsages[key] = glossaryUsages[key].concat(usage) | ||
}) | ||
}) | ||
|
||
/** | ||
* Create `_data/glossary-usages.json` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are you putting this stuff into JSON files rather than have Gatsby consume the data directly? You're basically using disc for caching data that takes almost no time to build. |
||
*/ | ||
await createFile( | ||
`./_data/glossary-usages.json`, | ||
JSON.stringify(glossaryUsages, undefined, 2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't do IO in the same method where you're doing data manipulation. Have your functions do one thing. It's easier to read, and easier to test. |
||
) | ||
} | ||
|
||
/** | ||
* Invoke | ||
*/ | ||
init() | ||
.then(() => console.info(`Completed: task: create:glossary\n`)) | ||
.catch(e => console.error(e)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
const globby = require('globby') | ||
const readFile = require('../utils/read-file') | ||
const createFile = require('../utils/create-file') | ||
|
||
/** | ||
* Init | ||
*/ | ||
const init = async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything I said about create-glossary pretty much applies here too. |
||
/** | ||
* Get all implementation reports | ||
*/ | ||
const reports = globby | ||
.sync([`./_data/implementations/*.json`]) | ||
.map(reportPath => { | ||
const fileContent = readFile(reportPath) | ||
return JSON.parse(fileContent) | ||
}) | ||
|
||
const implementers = [] | ||
const implementationsGroupedByRuleId = {} | ||
|
||
reports.forEach(report => { | ||
const { tool, organisation, data } = report | ||
|
||
/** | ||
* Create data that can be used in `src/templates/coverage.js` | ||
*/ | ||
implementers.push(report) | ||
|
||
/** | ||
* Iterate each implementation & group by rule id | ||
*/ | ||
data.forEach(({ ruleId, implementation }) => { | ||
if (!implementation) { | ||
return | ||
} | ||
|
||
/** | ||
* Note: | ||
* only build `metrics` for implementations that are `complete` | ||
*/ | ||
const { complete = false } = implementation | ||
if (complete) { | ||
return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can just do that in the first if statement. |
||
} | ||
|
||
if (!implementationsGroupedByRuleId[ruleId]) { | ||
implementationsGroupedByRuleId[ruleId] = [] | ||
} | ||
|
||
implementationsGroupedByRuleId[ruleId].push({ | ||
organisation, | ||
tool, | ||
implementation, | ||
}) | ||
}) | ||
}) | ||
|
||
/** | ||
* Create `implementations.json` | ||
*/ | ||
await createFile( | ||
`_data/implementers.json`, | ||
JSON.stringify(implementers, null, 2) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is just an array of other JSON files? That doesn't seem useful. |
||
) | ||
|
||
/** | ||
* Create metrics in `_data` for usage in `site` | ||
*/ | ||
await createFile( | ||
`_data/implementation-metrics.json`, | ||
JSON.stringify(implementationsGroupedByRuleId, null, 2) | ||
) | ||
} | ||
|
||
init() | ||
.then(() => console.info(`\nImplementation metrics generated.\n`)) | ||
.catch(e => { | ||
console.error(e) | ||
process.exit(1) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
const globby = require('globby') | ||
const makeDir = require('make-dir') | ||
const objectHash = require('object-hash') | ||
const codeBlocks = require('gfm-code-blocks') | ||
const { | ||
www: { url, baseDir }, | ||
} = require('./../package.json') | ||
const getMarkdownData = require('./../utils/get-markdown-data') | ||
const createFile = require('../utils/create-file') | ||
const regexps = require('../utils/reg-exps') | ||
const getAllMatchesForRegex = require('../utils/get-all-matches-for-regex') | ||
const copyTestcasesAssets = require('./testcases/copy-testcases-assets') | ||
const createTestcasesJson = require('./testcases/create-testcases-json') | ||
const createTestcasesOfRuleOfEmReportTool = require('./testcases/create-testcases-of-rule-of-em-report-tool') | ||
|
||
/** | ||
* Create test case files & other meta-data from test case in each rule. | ||
* | ||
* -> create test cases files into `./public/testcases/` | ||
* -> copy `./test-assets/*` into `./public` | ||
* -> create `testcases.json` into `./public` | ||
*/ | ||
const init = async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything I said about create-glossary pretty much applies here too. |
||
/** | ||
* Create `public` directory | ||
*/ | ||
await makeDir(`public`) | ||
|
||
/** | ||
* Get all rules `markdown` data | ||
*/ | ||
const rulesData = globby | ||
.sync([`./_rules/*.md`]) | ||
.map(rulePath => getMarkdownData(rulePath)) | ||
|
||
let allRulesTestcases = [] | ||
|
||
/** | ||
* iterate all rule pages | ||
* -> get code snippets | ||
* -> and their relevant titles | ||
*/ | ||
rulesData.forEach(ruleData => { | ||
const { frontmatter, body } = ruleData | ||
const { | ||
id: ruleId, | ||
name: ruleName, | ||
accessibility_requirements: ruleAccessibilityRequirements, | ||
} = frontmatter | ||
const codeTitles = getAllMatchesForRegex(regexps.testcaseTitle, body) | ||
|
||
/** | ||
* get code blocks in markdown body | ||
*/ | ||
const codeSnippets = codeBlocks(body) | ||
|
||
if (codeTitles.length !== codeSnippets.length) { | ||
throw new Error( | ||
`Number of matching titles for code snippets is wrong. Check markdown '${ruleName}' for irregularities.` | ||
) | ||
} | ||
|
||
/** | ||
* iterate each code snippet | ||
* -> create a testcase file | ||
* -> and add meta of testcase to `testcases.json` | ||
*/ | ||
const ruleTestcases = codeSnippets.reduce((out, codeBlock, index) => { | ||
const title = codeTitles[index] | ||
if (!title) { | ||
throw new Error('No title found for code snippet.') | ||
} | ||
|
||
const { code, block } = codeBlock | ||
let { type = 'html' } = codeBlock | ||
|
||
if (regexps.testcaseCodeSnippetTypeIsSvg.test(block.substring(0, 15))) { | ||
type = 'svg' | ||
} | ||
|
||
const codeId = objectHash({ | ||
block, | ||
type, | ||
ruleId, | ||
}) | ||
|
||
const titleCurated = title.value.split(' ').map(t => t.toLowerCase()) | ||
|
||
const testcaseFileName = `${ruleId}/${codeId}.${type}` | ||
const testcasePath = `testcases/${testcaseFileName}` | ||
/** | ||
* Create testcase file | ||
*/ | ||
createFile(`${baseDir}/${testcasePath}`, code) | ||
|
||
/** | ||
* Create meta data for testcase(s) | ||
*/ | ||
const testcase = { | ||
testcaseId: codeId, | ||
url: `${url}/${testcasePath}`, | ||
relativePath: testcasePath, | ||
expected: titleCurated[0], | ||
ruleId, | ||
ruleName, | ||
rulePage: `${url}/rules/${ruleId}`, | ||
ruleAccessibilityRequirements, | ||
} | ||
|
||
out.push(testcase) | ||
return out | ||
}, []) | ||
|
||
// add rule testcases to all testcases | ||
allRulesTestcases = allRulesTestcases.concat(ruleTestcases) | ||
|
||
/** | ||
* Create test cases of rule for use with `em report tool` | ||
*/ | ||
createTestcasesOfRuleOfEmReportTool({ | ||
ruleId, | ||
ruleName, | ||
ruleTestcases, | ||
ruleAccessibilityRequirements, | ||
}) | ||
}) | ||
|
||
/** | ||
* Copy `test-assets` that are used by `testcases` | ||
*/ | ||
await copyTestcasesAssets() | ||
|
||
/** | ||
* Generate `testcases.json` | ||
*/ | ||
await createTestcasesJson(allRulesTestcases) | ||
|
||
console.info(`\nGenerated Test Cases.\n`) | ||
} | ||
|
||
/** | ||
* Invoke | ||
*/ | ||
init() | ||
.then(() => console.log('Completed: task: create:testcases')) | ||
.catch(e => { | ||
console.error(e) | ||
process.write(1) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
const assert = require('assert') | ||
const program = require('commander') | ||
const { version } = require('../package.json') | ||
const createFile = require('../utils/create-file') | ||
const getFramedReport = require('./implementations/get-framed-report') | ||
const getImplementationForReport = require('./implementations/get-implementation-for-report') | ||
|
||
/** | ||
* Init | ||
* @param {Object} program program | ||
*/ | ||
const init = async program => { | ||
const { org, tool, path } = program | ||
|
||
/** | ||
* assert `args` | ||
*/ | ||
assert(org, '`Organisation` is required') | ||
assert(tool, '`tool` is required') | ||
assert(path, '`path` is required') | ||
|
||
console.info(`\nGet implementation of ${tool} by ${org}\n`) | ||
|
||
/** | ||
* fetch `report` & `frame` as required | ||
*/ | ||
const framedReport = await getFramedReport(path) | ||
|
||
/** | ||
* Get `implementation` | ||
*/ | ||
const data = await getImplementationForReport(framedReport) | ||
|
||
/** | ||
* create report | ||
*/ | ||
const report = { | ||
organisation: org, | ||
tool, | ||
data, | ||
} | ||
|
||
/** | ||
* Save `implementation` to `_data/implementations` | ||
*/ | ||
const filename = tool | ||
.split(' ') | ||
.join('-') | ||
.toLowerCase() | ||
await createFile( | ||
`_data/implementations/${filename}.json`, | ||
JSON.stringify(report, null, 2) | ||
) | ||
} | ||
|
||
/** | ||
* Parse `args` | ||
*/ | ||
program | ||
.version(version) | ||
.option('-o, --org <org>', 'Organisation, which created the EARL report') | ||
.option('-t, --tool <tool>', 'Tool used by EARL report') | ||
.option('-p, --path <path>', 'Path to EARL report') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
.parse(process.argv) | ||
|
||
/** | ||
* Init | ||
*/ | ||
init(program) | ||
.then(() => console.info(`\nImplementations data generated.\n`)) | ||
.catch(e => { | ||
console.error(e) | ||
process.exit(1) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests please.