├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── cli.js ├── config └── chrome.json ├── gallery.html ├── lib ├── gallery.js ├── helpers.js ├── logger.js ├── spider.js └── wraith.js ├── package.json └── readme.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = tabs 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .ds_store 4 | shots 5 | config 6 | spider.txt 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 4, 10 | "newcap": true, 11 | "noarg": true, 12 | "quotmark": "single", 13 | "regexp": true, 14 | "undef": true, 15 | "unused": true, 16 | "strict": true, 17 | "trailing": true, 18 | "smarttabs": true 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | deploy: 3 | provider: npm 4 | email: james@james-bell.co.uk 5 | api_key: 6 | secure: bPawPetp26/o+/aTL3DAMkOunuZUzzNs75sQNbk0OSmJEwHut/41s1XNewHMvt3NaKwa4+Cmxsi0mBepO609njZKvqgcj1v6gdB9YhYiJKRYiA3Il3JcpopPNz5CusGsH6l6m5A3gtUZLhjaLHc8sPgepd4uDS5WFZz/xoYfxtQ= 7 | on: 8 | repo: jamesryanbell/node-wraith 9 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | var moment = require('moment'); 5 | var program = require('commander'); 6 | var log = require('./lib/logger'); 7 | 8 | program.unknownOption = program.help; 9 | program 10 | .usage('[options] ') 11 | .version(require('./package').version) 12 | .option('-c, --config [config]', 'Specify the location of the configuration file') 13 | .option('-q, --quiet', 'All logging is hidden') 14 | .parse(process.argv); 15 | 16 | if(!program.config) { 17 | program.help(); 18 | } else { 19 | var wraith = require('./lib/wraith'); 20 | var config = require(program.config); 21 | var timer = false; 22 | 23 | if(program.quiet) { 24 | config.quiet = true; 25 | } else { 26 | timer = moment(); 27 | } 28 | 29 | new wraith(config, function() { 30 | if(timer) { 31 | log.info('Time Taken: ' + moment().diff(timer, 'seconds') + 's'); 32 | } 33 | log.success('Done'); 34 | }); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /config/chrome.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": "Test", 3 | "domains": [ 4 | "http://www.bbc.co.uk", 5 | "http://www.live.bbc.co.uk" 6 | ], 7 | "engines" : [ 8 | "phantomjs" 9 | ], 10 | "sizes": [ 11 | "320", 12 | "768", 13 | "1440" 14 | ], 15 | "paths": [ 16 | "/", 17 | "/tv", 18 | "/weather" 19 | ], 20 | "outputDir": "shots/bbc", 21 | "maxConnections": 20, 22 | "server": { 23 | "start": true, 24 | "port": 23423 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gallery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | Screenshots - {{project}} 18 | 19 | 20 |
21 | 24 |
25 |
26 |
27 |

Screenshots:

28 | 33 |
34 |
35 |
36 | {{#images}} 37 | {{#dir}} 38 |
39 | 40 |

/{{#resolve}}{{dir}}{{/resolve}}

41 |
42 | {{/dir}} 43 |
44 |

{{size}}px

45 |
46 | 47 | 48 | 49 |

{{#resolve}}{{base}}{{/resolve}}

50 |
51 |
52 | 53 | 54 | 55 |

{{#resolve}}{{compare}}{{/resolve}}

56 |
57 |
58 | 59 | 60 | 61 |

diff

62 |

{{#contents}}{{diff}}{{/contents}}%

63 |
64 |
65 | {{/images}} 66 |
67 |
68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /lib/gallery.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var mustache = require('mustache'); 6 | var helpers = require('./helpers'); 7 | var log = require('./logger'); 8 | var connect = require('connect'); 9 | var serveStatic = require('serve-static'); 10 | var open = require('open'); 11 | 12 | module.exports.generate = function(dirs, compareList, outputDir, project, server, cb) { 13 | compareList = compareList.sort(helpers.sortByProp('sort')); 14 | var template = path.join(__dirname, '/../gallery.html'); 15 | var view = { 16 | 'images' : compareList, 17 | 'dirs': dirs.sort(), 18 | 'project': project, 19 | 'resolve': function() { 20 | return function(text, render) { 21 | var rendered = render(text); 22 | return rendered.replace(///g, '/').replace(outputDir, '').replace(/^\//, ''); 23 | }; 24 | }, 25 | 'contents': function() { 26 | return function(text, render) { 27 | var rendered = render(text); 28 | var output = fs.readFileSync(rendered.replace(///g, '/').replace(/\/\//g,'/'),'utf8'); 29 | return output ? output : 0; 30 | }; 31 | } 32 | }; 33 | 34 | fs.readFile(template, function (err, data) { 35 | if (err) { throw err; } 36 | var output = mustache.render(data.toString(), view); 37 | fs.writeFile(path.join(outputDir, 'gallery.html'), output, function(err) { 38 | if(err) { 39 | log.error(err); 40 | } else { 41 | log.success('Gallery generated'); 42 | cb(); 43 | 44 | if(server.start) { 45 | connect().use(serveStatic(outputDir)).listen(server.port, function(){ 46 | open('http://localhost:' + server.port + '/gallery.html'); 47 | log.success('Server started on port ' + server.port); 48 | }); 49 | } 50 | } 51 | }); 52 | }); 53 | }; 54 | -------------------------------------------------------------------------------- /lib/helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.sortByProp = function(prop) { 4 | return function(a,b) { 5 | if(a[prop] > b[prop]) { 6 | return 1; 7 | } else if( a[prop] < b[prop] ) { 8 | return -1; 9 | } 10 | return 0; 11 | }; 12 | }; 13 | 14 | module.exports.emptyFolder = function(folder, cb) { 15 | if(typeof(folder) === 'undefined') { return false; } 16 | 17 | var rimraf = require('rimraf'); 18 | rimraf(folder, function(err) { 19 | if( err ) { throw err; } 20 | cb(); 21 | }); 22 | return true; 23 | }; 24 | -------------------------------------------------------------------------------- /lib/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var chalk = require('chalk'); 4 | var logSymbols = require('log-symbols'); 5 | 6 | exports.error = function(msg, hide) { 7 | this.logger('error', logSymbols.error + ' ' + msg, hide); 8 | }; 9 | 10 | exports.warn = function(msg, hide) { 11 | this.logger('warn', logSymbols.warning + ' ' + msg, hide); 12 | }; 13 | 14 | exports.success = function(msg, hide) { 15 | this.logger('success', logSymbols.success + ' ' + msg, hide); 16 | }; 17 | 18 | exports.info = function(msg, hide) { 19 | this.logger('info', logSymbols.info + ' ' + msg, hide); 20 | }; 21 | 22 | exports.log = function(msg, hide) { 23 | this.logger('log', msg, hide); 24 | }; 25 | 26 | exports.logger = function(level, msg, hide) { 27 | var color = false; 28 | switch(level) { 29 | case 'error': 30 | color = chalk.red.bold; 31 | break; 32 | case 'warn': 33 | color = chalk.yellow.bold; 34 | break; 35 | case 'info': 36 | color = chalk.blue.bold; 37 | break; 38 | case 'success': 39 | color = chalk.green.bold; 40 | break; 41 | } 42 | if(!hide && !this.quiet) { 43 | if(color) { 44 | console.log(color(msg)); 45 | } else { 46 | console.log(msg); 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /lib/spider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var mkdirp = require('mkdirp'); 5 | var crawl = require('crawl'); 6 | var log = require('./logger'); 7 | 8 | module.exports.spider = function(url, file, callback) { 9 | 10 | log.log('Crawl of ' + url + ' started'); 11 | 12 | crawl.crawl(url, { headers: false, body: false }, function(err, pages) { 13 | 14 | if (err) { 15 | log.error('An error occured', err); 16 | return; 17 | } 18 | 19 | var txt = ''; 20 | var link = ''; 21 | var pagesLength = pages.length; 22 | 23 | for(var i = 0; i 0) { 34 | var folders = file.split('/'); 35 | folders.pop(); 36 | folders = folders.join('/'); 37 | 38 | mkdirp(folders, function (err) { 39 | if (err) { throw err; } 40 | fs.writeFile(file, txt, function(err) { 41 | if(err) { 42 | log.error(err); 43 | } else { 44 | log.log('Spider file saved to ' + file); 45 | callback(); 46 | } 47 | }); 48 | }); 49 | } else { 50 | log.error('No urls found'); 51 | process.exit(1); 52 | } 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /lib/wraith.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var async = require('async'); 5 | var mkdirp = require('mkdirp'); 6 | var path = require('path'); 7 | var resemble = require('node-resemble-js'); 8 | var progress = require('progress'); 9 | var gallery = require('./gallery'); 10 | var helpers = require('./helpers'); 11 | var log = require('./logger'); 12 | var phantomjs = require('phantomjs2'); 13 | 14 | function Wraith(config, cb) { 15 | var self = this; 16 | self.cb = cb; 17 | log.quiet = config.quiet; 18 | 19 | if(config.paths && config.paths.length > 0) { 20 | self.config(config); 21 | } else if(config.spider && config.spider !== '') { 22 | if(!fs.existsSync(config.spider)) { 23 | var spider = require('./spider'); 24 | 25 | spider.spider(config.domains[0], config.spider, function() { 26 | self.config(config); 27 | }); 28 | } else { 29 | self.config(config); 30 | } 31 | } else { 32 | log.error('You must specify either a list of pages or a location to store the spider file'); 33 | process.exit(1); 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | module.exports = Wraith; 40 | 41 | Wraith.prototype.config = function(config) { 42 | 43 | var self = this; 44 | 45 | self.domains = []; 46 | self.domainLabels = []; 47 | self.outputFolder = config.outputDir || 'shots/'; 48 | self.engines = config.engines || ['phantomjs']; 49 | self.maxConnections = config.maxConnections || 20; 50 | self.project = config.project || ''; 51 | self.sizes = []; 52 | self.dirs = []; 53 | self.quiet = config.quiet || false; 54 | self.server = {}; 55 | self.server.start = config.server.start || false; 56 | self.server.port = config.server.port || 9090; 57 | 58 | if(typeof config.paths !== 'undefined' && config.paths.length > 0 ) { 59 | self.urls = config.paths; 60 | } else { 61 | self.urls = fs.readFileSync(config.spider, 'utf8').split('\n'); 62 | } 63 | 64 | for(var domain in config.domains) { 65 | self.domains.push(config.domains[domain].replace(/\/+$/, '')); 66 | self.domainLabels.push(config.domains[domain].replace(/.*?:\/\//g, '')); 67 | } 68 | 69 | if(self.urls.length === 0 || self.domains.length === 0) { 70 | log.error('No url(s) provided'); 71 | process.exit(1); 72 | return false; 73 | } 74 | 75 | for(var url in self.urls) { 76 | var folder = path.join(self.outputFolder, self.urls[url].substring(1).replace(/\/+$/,'') + '/'); 77 | self.dirs.push(folder); 78 | } 79 | 80 | if( config.sizes && config.sizes.length > 0 ) { 81 | self.sizes = config.sizes; 82 | self.clean(); 83 | return self; 84 | } else { 85 | log.info('No sizes defined, using most popular from w3counter stats'); 86 | var w3counter = require('w3counter'); 87 | w3counter('res', function (err, data) { 88 | if (err) { throw err; } 89 | for(var val in data) { 90 | var size = data[val].item.slice(0, data[val].item.indexOf('x')); 91 | self.sizes.push(size); 92 | } 93 | self.clean(); 94 | return self; 95 | }); 96 | } 97 | }; 98 | 99 | Wraith.prototype.clean = function() { 100 | var self = this; 101 | log.info('Cleaned up old folders and files'); 102 | 103 | helpers.emptyFolder(self.outputFolder, function () { 104 | self.createFolders(); 105 | return self; 106 | }); 107 | }; 108 | 109 | Wraith.prototype.createFolders = function() { 110 | var self = this; 111 | 112 | async.each(self.dirs, function(folder, callback) { 113 | mkdirp(folder, function (err) { 114 | if (err) { throw err; } 115 | callback(); 116 | }); 117 | }, function(err) { 118 | if( err ) { 119 | log.error('An error occurred during folder creation'); 120 | process.exit(1); 121 | return self; 122 | } else { 123 | log.success('New folders created successfully'); 124 | self.takeScreenshots(); 125 | return self; 126 | } 127 | }); 128 | }; 129 | 130 | Wraith.prototype.takeScreenshots = function () { 131 | var self = this; 132 | var url = null; 133 | var imageUrls = []; 134 | var screenshotQueue = []; 135 | var size = null; 136 | var image = null; 137 | var folder = null; 138 | 139 | if( self.engines.length === 1 ) { 140 | for(var domain in self.domains) { 141 | for(url in self.urls) { 142 | imageUrls.push(self.domains[domain] + self.urls[url]); 143 | self.folder = path.join(self.outputFolder, self.urls[url].substring(1).replace(/\/+$/,'') + '/'); 144 | for(size in self.sizes) { 145 | image = self.folder + self.domainLabels[domain] + '_' + self.sizes[size] + '.png'; 146 | screenshotQueue.push({ 147 | engine: self.engines[0], 148 | url: self.domains[domain] + self.urls[url], 149 | size: self.sizes[size], 150 | output: image 151 | }); 152 | } 153 | } 154 | } 155 | } else { 156 | for(url in self.urls) { 157 | for(var engine in self.engines) { 158 | imageUrls.push(self.domains[0] + self.urls[url]); 159 | folder = path.join(self.outputFolder, self.urls[url].substring(1).replace(/\/+$/,'') + '/'); 160 | for(size in self.sizes) { 161 | image = folder + self.engines[engine] + '_' + self.sizes[size] + '.png'; 162 | screenshotQueue.push({ 163 | engine: self.engines[engine], 164 | url: self.domains[0] + self.urls[url], 165 | size: self.sizes[size], 166 | output: image 167 | }); 168 | } 169 | } 170 | } 171 | } 172 | 173 | log.info('Taking screenshots'); 174 | 175 | var bar = false; 176 | if(!this.quiet) { 177 | bar = new progress(':bar :elapseds :current/:total', {total: screenshotQueue.length}); 178 | } 179 | 180 | var webshot = require('webshot'); 181 | async.eachLimit(screenshotQueue, self.maxConnections, function(task, callback) { 182 | webshot(task.url, task.output, { 183 | windowSize: { 184 | width: task.size, 185 | height: 300 186 | }, 187 | shotSize: { 188 | width: task.size, 189 | height: 'all' 190 | }, 191 | defaultWhiteBackground: true, 192 | phantomPath: self.getEnginePath[task.engine], 193 | phantomConfig: { 194 | 'debug': 'false', 195 | 'load-images': 'true' 196 | } 197 | }, function(err) { 198 | if( err ) { 199 | log.error('An error occurred'); 200 | log.error(err); 201 | return self; 202 | } else { 203 | if(bar) { 204 | bar.tick(); 205 | } 206 | callback(); 207 | } 208 | }); 209 | 210 | }, function(err) { 211 | if( err ) { 212 | log.error('An error occurred during screenshotting'); 213 | process.exit(1); 214 | return self; 215 | } else { 216 | log.success('Screenshots done'); 217 | self.compareScreenshots(); 218 | return self; 219 | } 220 | }); 221 | }; 222 | 223 | Wraith.prototype.getEnginePath = function(engine) { 224 | if(engine == 'phantomjs') { 225 | return phantomjs.path; 226 | } else { 227 | return engine; 228 | } 229 | }; 230 | 231 | Wraith.prototype.compareScreenshots = function() { 232 | var self = this; 233 | var fileLabels = []; 234 | 235 | if( self.engines.length === 1 ) { 236 | fileLabels = [self.domainLabels[0], self.domainLabels[1]]; 237 | } else { 238 | fileLabels = [self.engines[0], self.engines[1]]; 239 | } 240 | 241 | self.compareQueue = []; 242 | 243 | for(var url in self.urls) { 244 | var dir = self.urls[url].substring(1).replace(/\/+$/,'') + '/'; 245 | var folder = path.join(self.outputFolder, dir); 246 | for(var size in self.sizes) { 247 | var item = { 248 | 'dir': ( size > 0 ? false : folder), 249 | 'sort': folder + size, 250 | 'base': folder + fileLabels[0] + '_' + self.sizes[size] + '.png', 251 | 'compare': folder + fileLabels[1] + '_' + self.sizes[size] + '.png', 252 | 'output': folder + self.sizes[size] + '_diff.png', 253 | 'diff': folder + self.sizes[size] + '_diff.txt', 254 | 'size': self.sizes[size] 255 | }; 256 | self.compareQueue.push(item); 257 | } 258 | } 259 | 260 | resemble.outputSettings({ 261 | errorColor: { 262 | red: 200, 263 | green: 0, 264 | blue: 0 265 | }, 266 | errorType: 'movementWithDistanceBasedIntensity', 267 | transparency: 0.4, 268 | largeImageThreshold: 0 269 | }); 270 | 271 | var bar = false; 272 | if(!this.quiet) { 273 | bar = new progress(':bar :elapseds :current/:total', { total: self.compareQueue.length }); 274 | } 275 | 276 | log.info('Comparing images'); 277 | async.eachLimit(self.compareQueue, self.maxConnections, function(task, callback) { 278 | resemble('./' + task.base).compareTo('./' + task.compare).ignoreAntialiasing().onComplete(function(data) { 279 | data.getDiffImage().pack().pipe(fs.createWriteStream(task.output)); 280 | fs.writeFile('./' + task.diff, data.misMatchPercentage, function(err) { 281 | if(err) { log.error(err); } 282 | if(bar) { 283 | bar.tick(); 284 | } 285 | callback(); 286 | }); 287 | }); 288 | }, function(err) { 289 | if( err ) { 290 | log.log(err); 291 | log.error('An error occurred during comparison'); 292 | process.exit(1); 293 | return self; 294 | } else { 295 | log.success('Image comparison done'); 296 | self.generateGallery(); 297 | return self; 298 | } 299 | }); 300 | }; 301 | 302 | Wraith.prototype.generateGallery = function() { 303 | var self = this; 304 | gallery.generate(self.dirs, self.compareQueue, self.outputFolder, self.config.project, self.server, self.cb); 305 | }; 306 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wraith", 3 | "version": "2.2.0", 4 | "description": "A responsive screenshot comparison tool. Based on the Ruby version available at http://github.comm/BBC-News/wraith", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/jamesryanbell/node-wraith" 9 | }, 10 | "bin": { 11 | "wraith": "cli.js" 12 | }, 13 | "main": "lib/wraith.js", 14 | "author": { 15 | "name": "James Bell", 16 | "email": "james@james-bell.co.uk", 17 | "url": "http://james-bell.co.uk" 18 | }, 19 | "engines": { 20 | "node": ">=0.10.0" 21 | }, 22 | "scripts": {}, 23 | "files": [ 24 | "index.js", 25 | "cli.js", 26 | "lib/*", 27 | "gallery.html", 28 | "snap.js" 29 | ], 30 | "keywords": [ 31 | "page", 32 | "website", 33 | "site", 34 | "web", 35 | "url", 36 | "resolution", 37 | "size", 38 | "screenshot", 39 | "screen", 40 | "responsive", 41 | "cli", 42 | "wraith", 43 | "phantomjs", 44 | "triflejs", 45 | "slimerjs" 46 | ], 47 | "dependencies": { 48 | "async": "~0.2.10", 49 | "chalk": "~0.4.0", 50 | "commander": "^2.5.1", 51 | "crawl": "~0.3.1", 52 | "log-symbols": "^1.0.1", 53 | "mkdirp": "~0.3.5", 54 | "moment": "^2.8.4", 55 | "mustache": "~0.8.1", 56 | "node-resemble-js": "0.0.4", 57 | "phantomjs2": "^2.2.0", 58 | "progress": "^1.1.8", 59 | "rimraf": "~2.2.6", 60 | "w3counter": "^1.0.3", 61 | "webshot": "^0.15.3", 62 | "connect": "^3.3.5", 63 | "open": "^0.0.5", 64 | "serve-static": "^1.9.3" 65 | }, 66 | "devDependencies": { 67 | "jshint": "^2.5.11", 68 | "jshint-stylish": "^1.0.0" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Wraith 2 | A responsive screenshot comparison tool. It can be used for regression testing or cross browser testing 3 | 4 | Based on the Ruby version available at [http://github.com/BBC-News/wraith](http://github.com/BBC-News/wraith) 5 | 6 | ## CLI app 7 | 8 | ### Install 9 | 10 | ``` 11 | npm install -g wraith 12 | ``` 13 | 14 | ###Usage 15 | 16 | Usage: 17 | wraith --config 18 | 19 | Options: 20 | -h, --help Output help information 21 | -v, --version Output version information 22 | -c, --config [config] Specify the location of the configuration file 23 | -q, --quiet All logging is hidden 24 | 25 | Examples: 26 | wraith --config ./config/chrome 27 | 28 | ### Configuration file 29 | 30 | Wraith uses a json based configuration file that allows you specify a large number of options. You can create as many configurations files as you need and call them from the cli using the --config flag. 31 | 32 | Below is an example configuration file: 33 | 34 | { 35 | Optional name for the project, if supplied it will be used within the generated 36 | gallery only 37 | "project": "Test", 38 | 39 | Specify one or two domains 40 | "domains": [ 41 | "http://www.bbc.co.uk", 42 | "http://live.bbc.co.uk" 43 | ], 44 | 45 | Engines supported are phantomjs and slimerjs but in theory any phantomjs based 46 | headless browser can be supported. 47 | 48 | To do cross browser testing specify one domain and two engines or your choice 49 | "engines": [ 50 | "phantomjs" 51 | ], 52 | 53 | Specify as many sizes as you wish. If no sizes are specified then the most 54 | popular sizes will be used based on the information provided by 55 | w3counter.com stats 56 | "sizes": [ 57 | "320", 58 | "768", 59 | "1440" 60 | ], 61 | 62 | Output directory within the /shots directory 63 | "outputDir": "test/chrome/", 64 | 65 | You can specify a list of paths to be used or you can crawl the site. 66 | Note: If paths are provided they will take precedent and the spider file 67 | will be ignored. 68 | 69 | "paths": [ 70 | "/", 71 | "/news/", 72 | "/news/local/", 73 | "/news/england/york_and_north_yorkshire/", 74 | "/weather/" 75 | ], 76 | 77 | If no paths are specified then a site crawl will take place and the results 78 | will be save in the location specified within this option 79 | "spider": "spider/test.txt", 80 | 81 | Limit the amount of concurrent processes 82 | "maxConnections": 20 83 | } 84 | 85 | ###API 86 | This is very basic at the minute but you can use wraith within your applications. 87 | 88 | At the minute there is only one option which is to run the application with a given config file and then run a specified callback but I am looking to expand this in the future. 89 | 90 | npm install wraith --save-dev 91 | 92 | var wraith = require('wraith'); 93 | var config = require('./config/chrome'); 94 | 95 | new Wraith(config, function() { 96 | console.log('Callback, do something else here!'); 97 | }); 98 | 99 | As the config is just an object you can change it at any point before calling 100 | the wraith function e.g. 101 | 102 | config.maxConnections = 10; 103 | config.quiet = true; 104 | 105 | new Wraith(config, function() { 106 | console.log('Callback, do something else here!'); 107 | }); 108 | 109 | ## License 110 | 111 | MIT © [James Bell](http://james-bell.co.uk) 112 | --------------------------------------------------------------------------------