API Docs for: 0.0.6
Show:

File: lib/commands/image.js

'use strict';

// ext. libs
var Q = require('q');
var fs = require('fs');
var PNG = require('node-pngjs').PNG;

/**
 * Execute related methods
 *
 * @module Driver
 * @class Image
 * @namespace Dalek.DriverNative.Image
 */

var Image = {
  imagecompareResults : {
    equal         : 'equal',
    sizeDifferent : 'images have different sizes',
    imageDiffent  : 'images are different'
  },

  imagecompare : function (opts, expected, makediff, hash) {
    this.actionQueue.push(this._imagecompareCb.bind(this, opts, expected, makediff, hash));
    return this;
  },

  imagecut : function (opts, hash) {
    var position = {};

    this.actionQueue.push(this.webdriverClient.element.bind(this.webdriverClient, opts.selector));
    this.actionQueue.push(this.webdriverClient.location.bind(this.webdriverClient, opts.selector));

    /* get left top positions of element*/
    this.actionQueue.push(function (result) {
      var value = JSON.parse(result).value;
      position.x = Math.ceil(value.x);
      position.y = Math.ceil(value.y);
    });

    this.actionQueue.push(this.webdriverClient.size.bind(this.webdriverClient, opts.selector));
    this.actionQueue.push(this._imagecutCb.bind(this, opts, hash, opts.selector, position));

    return this;
  },

  _imagecutCb: function (opts, hash, selector, position, result) {
    var rect;
    var size = JSON.parse(result);
    var deferred = Q.defer();

    rect = this._convertToRect(position, size.value);
    var cb = function () {
      this.events.emit('driver:message', {key: 'imagecut', value: selector, uuid: hash, hash: hash});
      deferred.resolve();
    };

    this._doImagecut(opts.realpath, opts.selector, rect, cb.bind(this));

    return deferred.promise;
  },

  _doCalcSize: function (dstPos, dstSize, srcSize) {
    if (dstPos + dstSize <= srcSize) {
      return dstSize;
    } else if (dstSize <= srcSize) {
      return srcSize - dstPos;
    }
    return 0;
  },

  _doImagecut: function (path, element, rect, cb) {
    this._parsePNG(path).then(function (image) {
      var width = this._doCalcSize (rect.x, rect.width, image.png.width);
      var height = this._doCalcSize (rect.y, rect.height, image.png.height);

      var pngDst = new PNG({
        width :width,
        height:height
      });

      if (width === 0 || height === 0) {
        cb();
        return 'Error in image cutting. Size of element is bigger of size image.';
      }

      image.png.bitblt(pngDst, rect.x, rect.y, width, height, 0, 0);
      this._savePNG(pngDst, image.path).then(cb);

    }.bind(this));
  },

  _imagecompareCb: function (opts, expected, makediff, hash) {
    var deferred = Q.defer();
    var current = opts.realpath;

    var cb = function (result) {
      this.events.emit('driver:message', {key: 'imagecompare', value: result, uuid: hash, hash: hash});
      deferred.resolve();
    };

    this._doImagecompare(current, expected, makediff, cb.bind(this));

    return deferred.promise;
  },
  _doImagecompare: function (actualPath, etalonPath, makediff, cb) {
    var promises = [this._parsePNG(actualPath), this._parsePNG(etalonPath)];

    Q.all(promises)
    .spread(function (parsedImageA, parsedImageB) {
      var result = this._checkIfImagesTheSameAndMakeDiff(parsedImageA, parsedImageB, makediff);
      if (result === this.imagecompareResults.imageDiffent && makediff) {
        this._doSaveImageDiff(parsedImageB, result)
        .then(function (filePath) {
          cb(result + '. See differences by path ' + filePath);
        },
        cb);
      } else {
        cb(result);
      }
    }.bind(this),
    cb);
  },
  _doSaveImageDiff : function (image) {
    var imageDiffPath = image.path.replace('.png', '.diff.png');

    return this._savePNG(image.png, imageDiffPath);
  },
  _checkIfImagesTheSameAndMakeDiff : function (a, b, makediff) {

    if (a.data.length !== b.data.length) {
      return this.imagecompareResults.sizeDifferent;
    }

    var result = this.imagecompareResults.equal;

    for (var i = 0, len = a.data.length; i < len; i += 4) {
      if (a.data[i]     !== b.data[i] ||
          a.data[i + 1] !== b.data[i + 1] ||
          a.data[i + 2] !== b.data[i + 2]) {
        result = this.imagecompareResults.imageDiffent;
        if (makediff) {
          this._setErrorPixel(b.data, i);
        } else {
          return result;
        }
      }
    }

    return result;
  },
  _parsePNG: function (path) {
    var deferred = Q.defer();

    fs.createReadStream(path)
      .pipe(new PNG({
        filterType: 1
      }))
      .on('parsed', function() {

        var parsedImage = {
          path   : path,
          data   : this.data,
          png    : this
        };

        deferred.resolve(parsedImage);
      })
      .on('error', function (event) {
        deferred.reject(event);
      });

    return deferred.promise;
  },

  _savePNG : function (png, filePath) {
    var deferred = Q.defer();

    png
    .pack()
    .pipe(fs.createWriteStream(filePath, {flags: 'w'}))
    .on('error', function (errors) {
      deferred.reject(errors);
    })
    .on('close', function () {
      deferred.resolve(filePath);
    });

    return deferred.promise;
  },
  _setErrorPixel : function (dst, index) {
    var errorPixelColor = { // Color for Error Pixels. Between 0 and 255.
      red: 255,
      green: 0,
      blue: 255,
      alpha: 255
    };

    dst[index]     = errorPixelColor.red;
    dst[index + 1] = errorPixelColor.green;
    dst[index + 2] = errorPixelColor.blue;
    dst[index + 3] = errorPixelColor.alpha;
  },
  _convertToRect: function (position, size) {
    return {
      x:position.x,
      y:position.y,
      width : size.width,
      height: size.height
    };
  }
};

module.exports = function (DalekNative) {
  // mixin methods

  Object.keys(Image).forEach(function (fn) {
    DalekNative.prototype[fn] = Image[fn];
  });

  return DalekNative;
};