whyboris/karma-helpful-reporter

View on GitHub
lib/util/draw.js

Summary

Maintainability
B
4 hrs
Test Coverage
'use strict';

var clc = require('cli-color');
var write = require('./printers').write;
var shell = require('./shell').getInstance();

var colorPass;
var colorFail;
var colorSkip;
var colorGraphOutline = clc.xterm(7);

var frame = 0;

exports.setColorOptions = function (colorSettings) {
  colorPass = clc.bgXterm(colorSettings.colorPass).xterm(colorSettings.colorPass);
  colorFail = clc.bgXterm(colorSettings.colorFail).xterm(colorSettings.colorFail);
  colorSkip = clc.bgXterm(colorSettings.colorSkip).xterm(colorSettings.colorSkip);
};

// default animation is braille
// e.g. ⠋ ⠇ ⠦ ⠴ ⠸ ⠙
var progressAnimation = ['\u2807 ', '\u2826 ', '\u2834 ', '\u2838 ', '\u2819 ', '\u280b ',
                         '\u280f ', '\u2827 ', '\u2836 ', '\u283c ', '\u2839 ', '\u281b '];

exports.setAnimationStyle = function (style) {
  if (style === 'clock') {
    // alternative to use -- emoji clock
    // 🕓
    progressAnimation = ['\u{1F55B} ', '\u{1F550} ', '\u{1F551} ', '\u{1F552} ', '\u{1F553} ', '\u{1F554} ',
                         '\u{1F555} ', '\u{1F556} ', '\u{1F557} ', '\u{1F558} ', '\u{1F559} ', '\u{1F55A} ' ];
  } else if (style === 'braille2') {
    // alternative to use -- two character braille
    var progressAnimation = [ '\u2807 ', '\u2846 ', '\u28C4 ', '\u28C0\u2880', '\u2840\u28C0', ' \u28E0',
                              ' \u28B0', ' \u2838', ' \u2819', '\u2808\u2809', '\u2809\u2801', '\u280B ' ]
  }
};


function DrawUtil() {

  var width = Math.ceil(shell.getWidth() * 0.7) | 0;
  this.tick = 0;

  /**
   * Returns colorized bar for the chart 
   * @param {Object} stats - object containing 'success, failed, skipped, total'
   * @returns {string}     - colorized string containing success, fail, and skipped
   */
  this.drawProgressBar = function (stats) {
    // array of widths for pass, fail, and skip -- in that order
    var widths = new Array();
    // 5 characters reserved for animation area - leftOffset
    widths[0] = Math.ceil((width - 5) * stats.success / stats.total);
    widths[1] = Math.ceil((width - 5) * stats.failed  / stats.total);
    widths[2] = Math.ceil((width - 5) * stats.skipped / stats.total);

    var difference = width - (widths[0] + widths[1] + widths[2]);
    if (difference > 0) {
      // Subtract the overflow from the longest width
      widths[widths.indexOf(Math.max(widths))] -= difference;
    }

    return this.drawThisMany(widths[0], 'o', colorPass) +
           this.drawThisMany(widths[1], 'X', colorFail) +
           this.drawThisMany(widths[2], '-', colorSkip);
  };

  /**
   * Return a string of the same character with color and background color chosen
   * @param {number} n      - the number of times to repeat a character
   * @param {string} char   - the character to repeat
   * @param {color}  color  - the color to make the character
   * @returns {string}      - colorized string of n many of chosen string
   */
  this.drawThisMany = function (n, char, color) {
    var outputString = '';
    for (var i = 0; i < n; i++) {
      outputString += char;
    }
    return color(outputString);
  };

  /**
   * Return the appropriate animation frame
   * @return {string} the frame of the animation
   */
  this.animationFrame = function () {
    frame++;
    if (frame > 11) {
      frame -= 12;
    }
    return progressAnimation[frame];
  }

  // Create a long line for above and below the progress indicator; includes 5 char leftOffset
  var topLine =    '     ' + this.drawThisMany(width - 4, '_', colorGraphOutline);
  var bottomLine = '     ' + this.drawThisMany(width - 4, '‾', colorGraphOutline);

  /**
   * Renders the final chart with colors, animation, and final emoji on final run 
   * @param {Object} stats - object containing 'success, failed, skipped, total'
   * @returns {void}
   */
  this.drawChart = function (stats) {
    write(topLine + '\n');
    // Determine if `in progress` or final render
    if (stats.success + stats.failed + stats.skipped === stats.total) {
      write(' ' + this.finalEmoji(stats) + '   ' + this.drawProgressBar(stats) + ' \n');
    } else {
      write(' ' + this.animationFrame()  +  '  ' + this.drawProgressBar(stats) + ' \n');
    }
    write(bottomLine + '\n');
    this.cursorUp(3);
  };

  /**
   * Return appropriate emoji based on test results
   * @param {Object} stats - object containing 'success, failed, skipped, total'
   * @returns {string}     - single emoji character
   */
  this.finalEmoji = function (stats) {
    // default is hand `ok` symbol
    // 👌
    var result = '\u{1F44C}';

    if (stats.failed > stats.total / 2) {
      // smiling poo
      // 💩
      result = '\u{1F4A9}';
    } else if (stats.failed > stats.success) {
      // sad face
      // 😞
      result = '\u{1F61E}';
    } else if (stats.failed === 0 && stats.skipped !== 0) {
      // thumbs up
      // 👍
      result = '\u{1F44D}';
    } else if (stats.success === stats.total) {
      // grinning
      // 😁
      result = '\u{1F44D}';
    }

    return result;
  };

  this.cursorUp = function(n) {
    write(clc.move.up(n));
  };

  this.cursorDown = function(n) {
    for (var i = 0; i < n; i++ ) {
      write('\n');
    }
  };

}

exports.getInstance = function() {
  return new DrawUtil();
};