test.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // USAGE:
  2. // node test.js -r README.md (Checks whole file)
  3. // node test.js -r README.md -d temp.md (Checks just the diff)
  4. const fs = require('fs');
  5. //let colors = require('colors/safe');
  6. const chalk = require('chalk');
  7. let licenses = new Set();
  8. let pr = false;
  9. let readme;
  10. let diff;
  11. //Parse the command options and set the pr var
  12. function parseArgs(args) {
  13. if ( args.indexOf('-r', 2) > 0 ) {
  14. readme = fs.readFileSync(args[args.indexOf('-r', 2)+1], 'utf8')
  15. }
  16. if (args.indexOf('-d', 2) > 0) {
  17. pr = true;
  18. diff = fs.readFileSync(args[args.indexOf('-d', 2)+1], 'utf8');
  19. }
  20. if ( pr === true) {
  21. console.log(chalk.blue(`Running on PR. README.md: ${args[args.indexOf('-r', 2)+1]} diff: ${args[args.indexOf('-d', 2)+1]}`))
  22. }
  23. }
  24. // Function to find lines with entries
  25. function entryFilter(md) {
  26. const linepatt = /^\s{0,2}-\s\[.*`/;
  27. return linepatt.test(md);
  28. }
  29. // Function to find lines with licenses
  30. function licenseFilter(md) {
  31. const linepatt = /^- `.*` - .*/;
  32. return linepatt.test(md)
  33. }
  34. // Function to split lines into array
  35. function split(text) {
  36. return text.split(/\r?\n/);
  37. }
  38. // All entries should match this pattern. If matches pattern returns true.
  39. function findPattern(text) {
  40. const patt = /^\s{0,2}-\s\[.*?\]\(.*?\) (`⚠` )?- .{0,249}?\.( \(\[(Demo|Source Code|Clients)\]\([^)]*\)(, \[(Source Code|Clients)\]\([^)]*\))?(, \[(Source Code|Clients)\]\([^)]*\))*\))? \`.*?\` \`.*?\`$/;
  41. if (patt.test(text) === true) {
  42. return true;
  43. }
  44. return false;
  45. }
  46. // Parses SPDX identifiers from list of licenses
  47. function parseLicense(md) {
  48. const patt = /^- `(.*)` - .*/
  49. return patt.exec(md)[1]
  50. }
  51. //Tests '- [Name](http://homepage/)'
  52. function testMainLink(text) {
  53. let testA = /(^ {0,2}- \[.*?\]\(.*\))(?=.?-? ?\w)/;
  54. const testA1 = /(- \[.*?\]?\(?.*?\)?)( .*$)/;
  55. if (testA.test(text) === false) {
  56. let a1 = testA1.exec(text)[2];
  57. return chalk.red.underline(text.replace(a1, ''))
  58. }
  59. return chalk.green(testA.exec(text)[1])
  60. }
  61. //Tests '`⚠` - Short description, less than 250 characters.'
  62. function testDescription(text) {
  63. const testB = /( - .*\. )(?:(\(?\[?|\`))/;
  64. const testA1 = /(- \[.*?\]?\(?.*?\)?)( .*$)/;
  65. const testB2 = /((\(\[|\`).*$)/;
  66. if (testB.test(text) === false) {
  67. let b1 = testA1.exec(text)[1];
  68. let b2 = testB2.exec(text)[1];
  69. return chalk.red.underline(text.replace(b1, '').replace(b2, ''))
  70. }
  71. return chalk.green(testB.exec(text)[1])
  72. }
  73. //If present, tests '([Demo](http://url.to/demo), [Source Code](http://url.of/source/code), [Clients](https://url.to/list/of/related/clients-or-apps))'
  74. function testSrcDemCli(text) {
  75. let testC = text.search(/\(\[|\)\,|\)\)/);
  76. let testD = /(?<=\w. )(\(\[(Demo|Source Code|Clients)\]\([^)]*\)(, \[(Source Code|Clients)\]\([^)]*\))?(, \[(Source Code|Clients)\]\([^)]*\))*\))(?= \`?)/;
  77. const testD1 = /(^.*\.)(?= )/;
  78. const testD2 = /(\`.*\` \`.*\`$)/;
  79. if ((testC > -1) && (testD.test(text) === false)) {
  80. let d1 = testD1.exec(text)[1];
  81. let d2 = testD2.exec(text)[1];
  82. return chalk.red.underline(text.replace(d1+' ', '').replace(d2, ''))
  83. } else if (testC > -1) {
  84. return chalk.green(testD.exec(text)[1])
  85. }
  86. return ""
  87. }
  88. // Tests '`License` `Language`'
  89. function testLangLic(text) {
  90. const testD2 = /(\`.*\` \`.*\`$)/;
  91. let testE = testD2.test(text);
  92. const testE1 = /(^[^`]*)/;
  93. if (testE === false) {
  94. let e1 = testE1.exec(text)[1];
  95. return chalk.red.underline(text.replace(e1, ''))
  96. }
  97. return chalk.green(testD2.exec(text)[1])
  98. }
  99. //Runs all the syntax tests...
  100. function findError(text) {
  101. let res
  102. res = testMainLink(text)
  103. res += testDescription(text)
  104. res += testSrcDemCli(text)
  105. res += testLangLic(text)
  106. return res + `\n`
  107. }
  108. //Check if license is in the list of licenses.
  109. function testLicense(md) {
  110. const regex = /.*\`(.*)\` \`.*\`$/;
  111. return licenses.has(regex.exec(md)[1])
  112. }
  113. //Parses name from entry
  114. function parseName(md) {
  115. const regex = /^\W*(.*?)\W/
  116. return regex.exec(md)[1]
  117. }
  118. function entryErrorCheck() {
  119. const lines = split(readme); // Inserts each line into the entries array
  120. let totalFail = 0;
  121. let totalPass = 0;
  122. let total = 0;
  123. let failed = [];
  124. let entries = [];
  125. let diffEntries = [];
  126. if (lines[0] === "") {
  127. console.log(chalk.red("0 Entries Found"))
  128. process.exit(0)
  129. }
  130. for (let i = 0; i < lines.length; i ++) { // Loop through array of lines
  131. if (entryFilter(lines[i]) === true) { // filter out lines that don't start with * [)
  132. e = {};
  133. e.raw = lines[i];
  134. e.line = i
  135. entries.push(e);
  136. } else if (licenseFilter(lines[i]) === true) {
  137. licenses.add(parseLicense(lines[i]))
  138. }
  139. }
  140. if (pr === true) {
  141. console.log(chalk.cyan("Only testing the diff from the PR."))
  142. const diffLines = split(diff); // Inserts each line of diff into an array
  143. for (let l of diffLines) {
  144. if (entryFilter(l) === true) { // filter out lines that don't start with * [)
  145. e = {};
  146. e.raw = l;
  147. diffEntries.push(e);
  148. } else if (licenseFilter(l) === true) {
  149. licenses.add(parseLicense(l))
  150. }
  151. }
  152. total = diffEntries.length
  153. for (let e of diffEntries) {
  154. e.pass = true
  155. e.name = parseName(e.raw)
  156. if (!findPattern(e.raw)) {
  157. e.highlight = findError(e.raw);
  158. e.pass = false;
  159. console.log(`${e.highlight}`)
  160. }
  161. e.licenseTest = testLicense(e.raw);
  162. if (e.licenseTest === false) {
  163. e.pass = false;
  164. console.log(chalk.yellow(`${e.name}'s license is not on License list.`))
  165. }
  166. if (e.pass) {
  167. totalPass++
  168. } else {
  169. totalFail++
  170. }
  171. }
  172. } else {
  173. console.log(chalk.cyan("Testing entire README.md"))
  174. total = entries.length
  175. for (let e of entries) {
  176. e.pass = true
  177. e.name = parseName(e.raw)
  178. if (!findPattern(e.raw)) {
  179. e.highlight = findError(e.raw);
  180. e.pass = false;
  181. console.log(`${chalk.yellow(e.line)} ${e.highlight}`)
  182. }
  183. e.licenseTest = testLicense(e.raw);
  184. if (e.licenseTest === false) {
  185. e.pass = false;
  186. console.log(chalk.yellow(`${e.line} ${e.name}'s license is not on License list.`))
  187. }
  188. if (e.pass) {
  189. totalPass++
  190. } else {
  191. totalFail++
  192. }
  193. }
  194. }
  195. if (totalFail > 0) {
  196. console.log(chalk.blue(`\n-----------------------------\n`))
  197. console.log(chalk.green("The portion of the entry with an error ") + chalk.underline.red("will be underlined and RED") + `\n`)
  198. console.log(chalk.blue(`\n-----------------------------\n`))
  199. console.log(chalk.red(`${totalFail} Failed, `) + chalk.green(`${totalPass} Passed, `) + chalk.blue(`of ${total}`))
  200. console.log(chalk.blue(`\n-----------------------------\n`))
  201. process.exit(1);
  202. } else {
  203. console.log(chalk.blue(`\n-----------------------------\n`))
  204. console.log(chalk.green(`${totalPass} Passed of ${total}`))
  205. console.log(chalk.blue(`\n-----------------------------\n`))
  206. process.exit(0)
  207. }
  208. }
  209. parseArgs(process.argv)
  210. entryErrorCheck();