Plato on Github
Report Home
lib/commands/screenshot.js
Maintainability
65.05
Lines of code
253
Difficulty
33.76
Estimated Errors
1.83
Function weight
By Complexity
By SLOC
/*! * * Copyright (c) 2013 Sebastian Golasch * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ 'use strict'; // ext. libs var Q = require('q'); var fs = require('fs'); /** * Screenshot related methods * * @module Driver * @class Screenshot * @namespace Dalek.DriverNative.Commands */ var Screenshot = { /** * Makes an screenshot of the current page * * @method screenshot * @param {string} path Root directory path * @param {string} pathname Pathname of the screenshot path * @param {string} hash Unique hash of that fn call * @param {string} uuid Unique hash of that fn call * @chainable */ screenshot: function (path, pathname, hash, uuid) { this.actionQueue.push(this.webdriverClient.screenshot.bind(this.webdriverClient)); this.actionQueue.push(this._screenshotCb.bind(this, path, pathname, hash, uuid)); return this; }, /** * Sends out an event with the results of the `screenshot` call * and stores the screenshot in the filesystem * * @method _screenshotCb * @param {string} path Root directory path * @param {string} pathname Pathname of the screenshot path * @param {string} hash Unique hash of that fn call * @param {string} uuid Unique hash of that fn call * @param {string} result Serialized JSON result of the screenshot call * @return {object} promise Screenshot promise * @private */ _screenshotCb: function (path, pathname, hash, uuid, result) { var deferred = Q.defer(); // replace base64 metadata var base64Data = JSON.parse(result).value.replace(/^data:image\/png;base64,/,''); // replace placeholders var realpath = this._replacePathPlaceholder(path + pathname); // check if we need to add a new directory this._recursiveMakeDirSync(realpath.substring(0, realpath.lastIndexOf('/'))); // write the screenshot fs.writeFileSync(realpath, base64Data, 'base64'); this.events.emit('driver:message', {key: 'screenshot', value: realpath, uuid: hash, hash: hash}); deferred.resolve(); return deferred.promise; }, /** * Recursige mkdir helper * * @method _recursiveMakeDirSync * @param {string} path Path to create * @private */ _recursiveMakeDirSync: function (path) { var normalizedPath = require('path').normalize(path); var pathSep = require('path').sep; var dirs = normalizedPath.split(pathSep); var root = ''; while (dirs.length > 0) { var dir = dirs.shift(); if (dir === '') { root = pathSep; } if (!fs.existsSync(root + dir)) { fs.mkdirSync(root + dir); } root += dir + pathSep; } }, /** * Return the formatted os name * * @method _parseOS * @param {string} Pathname * @return {string} Formatted pathname * @private */ _replacePathPlaceholder: function (pathname) { pathname = pathname.replace(':browser', this.browserName); pathname = pathname.replace(':version', this._parseBrowserVersion(this.sessionStatus.version)); pathname = pathname.replace(':timestamp', Math.round(new Date().getTime() / 1000)); pathname = pathname.replace(':osVersion', this._parseOSVersion(this.driverStatus.os.version)); pathname = pathname.replace(':os', this._parseOS(this.driverStatus.os.name)); pathname = pathname.replace(':datetime', this._parseDatetime()); pathname = pathname.replace(':date', this._parseDate()); pathname = pathname.replace(':viewport', this._parseViewport()); return pathname; }, /** * Return the formatted os name * * @method _parseOS * @return {string} OS name * @private */ _parseOS: function (os) { var mappings = { 'mac': 'OSX', 'Mac OS X': 'OSX' }; return mappings[os] || 'unknown'; }, /** * Return the formatted os version * * @method _parseOSVersion * @return {string} OS version * @private */ _parseOSVersion: function (version) { var vs = version.replace(/[^0-9\\.]/g, ''); vs = vs.replace(/\./g, '_'); return vs; }, /** * Return the formatted browser version * * @method _parseBrowserVersion * @return {string} Browser version * @private */ _parseBrowserVersion: function (version) { return version.replace(/\./g, '_'); }, /** * Return the ISO 8601 formatted date * * @method _parseDate * @param {object} optional A javascript date object. If undefined, the current date is used. * @return {string} Date * @private */ _parseDate: function (date) { date = date ? date : new Date(); var day = date.getDate(); var month = date.getMonth() + 1; var year = date.getFullYear(); month = ('0' + month).slice(-2); day = ('0' + day).slice(-2); return year + '-' + month + '-' + day; }, /** * Return the ISO 8601 formatted datetime * * @method _parseDatetime * @param {object} optional A javascript date object. If undefined, the current date is used. * @return {string} Datetime * @private */ _parseDatetime: function (date) { date = date ? date : new Date(); var dateStr = this._parseDate(); var timeStr = 'T#hours#:#minutes#:#seconds##formattedOffset#'; var hours = date.getHours(); var minutes = date.getMinutes(); var seconds = date.getSeconds(); var offsetHours = '' + Math.ceil(date.getTimezoneOffset() / 60); var offsetMinutes = '' + Math.abs(date.getTimezoneOffset() % 60); var formattedOffset; hours = ('0' + hours).slice(-2); minutes = ('0' + minutes).slice(-2); seconds = ('0' + seconds).slice(-2); if (offsetHours[0] === '-' && offsetHours.length < 3) { formattedOffset = offsetHours[0] + '0' + offsetHours[1]; } else if (offsetHours.length < 2) { formattedOffset = '+0' + offsetHours; } if (offsetMinutes.length < 2) { formattedOffset += ':0' + offsetMinutes; } else { formattedOffset += ':' + offsetMinutes; } if (offsetHours === '0' && offsetMinutes === '0') { formattedOffset = 'Z'; } timeStr = timeStr.replace('#hours#', hours); timeStr = timeStr.replace('#minutes#', minutes); timeStr = timeStr.replace('#seconds#', seconds); timeStr = timeStr.replace('#formattedOffset#', formattedOffset); return dateStr + timeStr; }, /** * Return the formatted viewport * * @method _parseViewport * @return {string} Viewport * @private */ _parseViewport: function () { var viewport = this.config.get('viewport'); return 'w' + viewport.width + '_h' + viewport.height; } }; /** * Mixes in screenshot methods * * @param {Dalek.DriverNative} DalekNative Native driver base class * @return {Dalek.DriverNative} DalekNative Native driver base class */ module.exports = function (DalekNative) { if ('undefined' === typeof DalekNative) { return Screenshot; } // mixin methods Object.keys(Screenshot).forEach(function (fn) { DalekNative.prototype[fn] = Screenshot[fn]; }); return DalekNative; };