All files / packages/design-system/scripts/gulp/accessibility vnu.js

0% Statements 0/39
0% Branches 0/6
0% Functions 0/11
0% Lines 0/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                                                                                                                                                                                   
// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license
 
import { exec } from 'child_process';
import fs from 'fs';
import gulp from 'gulp';
import _ from 'lodash';
import through from 'through2';
import jar from 'vnu-jar/vnu-jar';
import Vinyl from 'vinyl';
 
// where to write. Normally we'd move control to the caller, but this
// the pattern for all other gulp scripts
const IGNORE = [
  /(.*)_JAVA_OPTIONS(.*)/i, // "Travis outputs this to make this interesting"
  /Warning:(.*)/i, // Warnings shouldn't be considered errors
  /(.*)role=gridcell(.*)must be contained in, or owned by, an element with(.*)role=row/, // "An element with “role=gridcell“ must be contained in, or owned by, an element with “role=row“",
  /(.*)listbox(.*)for attribute(.*)aria-haspopup/, // "Bad value “listbox“ for attribute “aria-haspopup“ on element “div“.",
  /(.*)dialog(.*)for attribute(.*)aria-haspopup/, // "Bad value “dialog“ for attribute “aria-haspopup“ on element “div“.",
  /tree-grid(.*)tr(.*)is missing one or more of the following attributes:(.*)role/, // "Element “tr“ is missing one or more of the following attributes"
  /tree-grid(.*)tr(.*)is missing required attribute(.*)role/, // Element “tr” is missing required attribute “role”.
  /(.*)aria-expanded(.*)not allowed on element(.*)a(.*) at this point/, // Attribute “aria-expanded” not allowed on element “a” at this point.
];
 
const lint = function (dir, opt, cb) {
  let output = '';
  let vnu = 'java -jar ' + jar;
 
  const options = _.assign(
    {
      'errors-only': true,
      format: 'gnu',
      html: false,
      'no-stream': false,
      verbose: false,
    },
    opt,
  );
 
  let filesToValidate = '';
  dir.forEach((file) => {
    filesToValidate += file + ' ';
  });
 
  // Set options
  Object.keys(options).forEach((key) => {
    let val = options[key];
    if (key === 'format' && val !== 'gnu') vnu += '--format ' + val + ' ';
    if (val === true) vnu += ' --' + key + ' ';
  });
  console.log(vnu, filesToValidate);
  const child = exec(`${vnu} ${filesToValidate}`, { maxBuffer: Infinity }, (e) => cb(e, output));
 
  child.stderr.on('data', function (data) {
    output += data;
  });
};
 
const parseLine = (file) => {
  const rest = _.last(file.split('/.html/'));
  const name = rest.split('.html')[0];
  const [_name, line, type, val] = rest.split(':');
  return { name, line, val, type: String(type).trim() };
};
 
const report = (output) =>
  _(output.split('\n'))
    .flatMap(parseLine)
    .filter((x) => x.name)
    .groupBy('name')
    .mapValues((groups) => groups.map(({ line, type, val }) => ({ [line]: { [type]: val } })))
    .value();
 
export default (paths) => {
  const stream = through.obj();
  lint(paths, {}, (err, output) => {
    const vnuOutput = String(output)
      .split('\n')
      .filter((line) => !IGNORE.some((ignore) => line.match(ignore)))
      .join('\n');
 
    if (vnuOutput) {
      console.log('-----VNU ERROR----');
      console.log(vnuOutput);
      throw new Error('Html Failure (Vnu)');
    }
    const contents = JSON.stringify(report(vnuOutput), null, 2);
    stream.write(
      new Vinyl({
        path: 'vnu_report.json',
        contents: Buffer.from(contents),
      }),
    );
    stream.end();
  });
  return stream.pipe(gulp.dest('.reports/'));
};