Skip to content

Commit

Permalink
Merge pull request #7 from gardr/configuration
Browse files Browse the repository at this point in the history
More configuration per instrumentation
  • Loading branch information
sveisvei committed Mar 31, 2014
2 parents 0782f88 + c050dba commit 5bef353
Show file tree
Hide file tree
Showing 67 changed files with 1,577 additions and 1,064 deletions.
102 changes: 99 additions & 3 deletions README.md
@@ -1,18 +1,114 @@
Validator
garðr-validator
=========

Collect data from a display-ads lifecyle and validate the data. This project tries to give a nice framework for doing this.

[![Build Status](https://travis-ci.org/gardr/validator.png)](https://travis-ci.org/gardr/validator)
[![Coverage Status](https://coveralls.io/repos/gardr/validator/badge.png)](https://coveralls.io/r/gardr/validator)
[![NPM version](https://badge.fury.io/js/gardr-validator.png)](http://badge.fury.io/js/gardr-validator)
[![Dependency Status](https://david-dm.org/gardr/validator.png)](https://david-dm.org/gardr/validator)
[![devDependency Status](https://david-dm.org/gardr/validator/dev-status.png)](https://david-dm.org/gardr/validator#info=devDependencies)

# Installation
#### Installation

$ npm install

# Contributing
### Examples

See the web-gui for this project: https://github.com/gardr/validator-web/blob/master/lib/routes/validate.js#L284


#### Writing intrumentation and validators

##### Intrument / Hook example filename: 'someData.js':
module.exports = {
'onBeforeExit': function (api, config) {
api.switchToIframe();
if (config.someConfigBoolean){
api.set('collectedData', api.evaluate(function(config){
return window.someData;
}, config));
}
}
};

##### Preprocessor example filename 'fixSomethingAsync.js'
module.exports = {
'dependencies': ['someData'],
'preprocess': function(harvested, output, next, globalOptions){
output('someKey', {data: harvested.someData||{}});
setTimeout(next, 1);
}
};

##### Validator example filename 'someData.js'
module.exports = {
'preprocessors': [
'fixSomethingAsync'
],
'dependencies': [
'someData'
],
'validate': function(harvested, report, next, globalOptions){
if (this.someConfigBoolean){
if (harvested.someData){
report.error('Some message');
}
}
}
};

##### Adding instrumentation/hooks etc to a run

var run = require('gardr-validator');
var options = {
'include': [
{
name: 'someData',
path: '/resolved/path/to/someData.js'
}
],
'config':{
'someData': {
'someConfigBoolean': true
}
}
};
run(options, function(phantomError, harvest, report){
if (phantomError){
// do something
return;
}
assert(harvest.someData);
assert(harvest.someKey);
assert(report.errors.length === 1);
})

#### Options to runner
{
instrument: [
'actions', // defaults to files in /lib/rule/instrument/actions.js
{name: 'css'},
{name: 'custom', path: '/absolute/path/to/file'},
{name: 'custom2', code: 'var someCode = "";'}
],
preprocess: [
//..
],
validate: [
//..
]
//rest of runner default options, see /config folder.
}


#### Contributing
YES, pull requests with tests. Be sure to create a issue and let us know you are working on it - maybe we can help out with insights etc.

## Running tests

$ npm test

##### Alternatives

(please let us know of alternatives to this project)
72 changes: 72 additions & 0 deletions config/config.js
@@ -0,0 +1,72 @@
var pathLib = require('path');

function resolve(url) {
var args = [__dirname, '..', 'lib', 'phantom'].concat(url.split('/'));
var result = pathLib.join.apply(null, args);
return pathLib.resolve(result);
}

var validatorConfig = require('./validatorConfig.js');

module.exports = {
parentUrl: resolve('resources/parent.html'),
iframeUrl: resolve('resources/iframe.html'),


validatorBase: null,

instrument: [
// 'errors', // common
// 'har', // common
// 'log',
// 'actions',
// 'css',
// 'script',
// 'screenshots',
// 'timers',
// 'jquery',
// 'gardr',
// 'touch'
],
preprocess: [
'screenshots',
'har'
],
validate : [
'common',
'log' ,
'css' ,
'timers',
'jquery',
'gardr',
'sizes',
'codeUsage',
'touch'
],


// config for hooks etc, namespace for convenience.
config: validatorConfig,

viewport: {
width: 980,
height: 225
},

width: {
min: 980,
max: 980
},
height: {
min: 225,
max: 225
},

// used for requests - fetching new resources
headers: {
'Cache-Control': 'no-cache',
'Accept-Encoding': 'identify'
},
pageRunTime: 12000,
userAgent: 'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25'
};
75 changes: 75 additions & 0 deletions config/validatorConfig.js
@@ -0,0 +1,75 @@
module.exports = {
actions: {
trigger: {
click: true,
mouseover: true
},
trackWindowOpen: true
},
screenshots: {
ms: 25,
onlyUnique: true
},
scripts: {
collectAttributes: true
},
codeUsage: {
geolocation: {
active: true,
trackAfterInteraction: false
}
},
css: {
strictRules: true,
filterOutStyleTagsWith: '* { padding: 0; margin: 0; border: 0; }'
},
errors: {
//allowedErrors: 0
},
gardr: {
// takes input from viewportOptions
iframeNotAllowed: true,
enforceStyling: true,
enforceSpec: true,
illegalTags: ['meta[name=\"viewport\"]']
},
jquery: {
versionsBack: 1,
wrapAnimate: true
},
log: {
//output logs to view maybe?
},
sizes: {
//refetchResources: true, // processReources.js
//filterAfterUserInteraction: true, // onHalfTime triggers actions
thresholdBytes: 100000, // bytes gziped
giveExtraThreshold: {
jQuery: true,
jQueryThreshold: 33369,
AdForm: true
},
minimumPayloadSize: 100,
maxRequests: {
style: 0,
script: 2,
errors: 0,
image: 20,
other: 3
}
},
timers: {
nameToTriggerWrap: 'iframe.htm',
setTimeout: 20,
setInterval: 1,
requestAnimationFrame: 0
},
touch: {
swipeTop: true,
swipeRight: true,
swipeLeft: true,
frames: 20,
swipeTime: 250,
delayBeforeNext: 1800
}
};
4 changes: 3 additions & 1 deletion index.js
@@ -1 +1,3 @@
module.exports = require('./lib/index.js').run;
var lib = require('./lib/index.js');
module.exports = lib.run;
module.exports.defaults= lib.defaults;
66 changes: 39 additions & 27 deletions lib/helpers.js
@@ -1,38 +1,50 @@
var path = require('path');
var fs = require('fs');
var pathLib = require('path');
var async = require('async');

var RULE_RUNNER_BASE = path.join(__dirname, '.', 'rule');
var HOOK_BASE = path.join(RULE_RUNNER_BASE, 'hook');
var VALIDATOR_BASE = path.join(RULE_RUNNER_BASE, 'validator');
var PRE_BASE = path.join(RULE_RUNNER_BASE, 'preprocessor');
var internals = {};

function collect(base){
return function(spec){
return Object.keys(spec).map(function (key) {
var res;
if (typeof spec[key] === 'string') {
res = spec[key];
} else {
// default to validator root dirname
res = path.join(base, key + '.js');
}
return res;
});
internals.mapEntry = function (type) {
return function (entry) {
if (typeof entry === 'object') {
// maybe validate???
return entry;
}

if (typeof entry === 'string') {
return {
'name': entry,
'path': pathLib.join(__dirname, '.', 'rule', type, entry + '.js')
};
} else {
throw new Error('Wrong configuration of includes');
}
};
}
};

var collectSpec = collect(HOOK_BASE);
var collectValidator = collect(VALIDATOR_BASE);
var collectPreprocessor = collect(PRE_BASE);
internals.collect = function (type) {
return function (specList) {
if (!Array.isArray(specList)){
console.log('specList'.red, specList);
throw new TypeError('Should send in a list');
}
return specList.map(internals.mapEntry(type));
};
};

function statFiles(list, done) {
internals.statFiles = function (list, done) {
return async.map(list, fs.stat, done);
}
};

module.exports = {
collectSpec: collectSpec,
collectValidator: collectValidator,
collectPreprocessor: collectPreprocessor,
statFiles: statFiles
'mapEntry': internals.mapEntry,
'collect': function (parent) {
parent.instrument = internals.collect('instrument')(parent.instrument);
parent.preprocess = internals.collect('preprocess')(parent.preprocess);
parent.validate = internals.collect('validate')(parent.validate);
},
'collectSpec': internals.collect('instrument'),
'collectValidator': internals.collect('validate'),
'collectPreprocessor': internals.collect('preprocess'),
'statFiles': internals.statFiles
};

0 comments on commit 5bef353

Please sign in to comment.