114 changes: 72 additions & 42 deletions src/parse/spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,92 @@ var dl = require('datalib'),
Model = require('../core/Model'),
View = require('../core/View');

function parseSpec(spec, callback) {
var vf = arguments[arguments.length-1],
viewFactory = arguments.length > 2 && dl.isFunction(vf) ? vf : View.factory,
config = arguments[2] !== viewFactory ? arguments[2] : {},
model = new Model(config);
/**
* Parse graph specification
* @param spec (object)
* @param config (optional object)
* @param viewFactory (optional function)
* @param callback (error, model)
*/
function parseSpec(spec /*, [config,] [viewFactory,] callback */) {
// do not assign any values to callback, as it will change arguments
var arglen = arguments.length,
argidx = 2,
cb = arguments[arglen-1],
model = new Model(),
viewFactory = View.factory;

if (arglen > argidx && dl.isFunction(arguments[arglen - argidx])) {
viewFactory = arguments[arglen - argidx];
++argidx;
}
if (arglen > argidx && dl.isObject(arguments[arglen - argidx])) {
model.config(arguments[arglen - argidx]);
}

function onDone(err, value) {
if (cb) {
if (cb.length > 1) cb(err, value);
else if (!err) cb(value);
cb = null;
}
}

function onError(err) {
log.error(err);
onDone(err);
}

function onCreate(err) {
if (err) onError(err);
else onDone(null, viewFactory(model));
}

function parse(spec) {
// protect against subsequent spec modification
spec = dl.duplicate(spec);

var parsers = require('./'),
create = function() { callback(viewFactory(model)); },
width = spec.width || 500,
height = spec.height || 500,
padding = parsers.padding(spec.padding);

// create signals for width, height and padding
model.signal('width', width);
model.signal('height', height);
model.signal('padding', padding);

// initialize model
model.defs({
width: width,
height: height,
padding: padding,
viewport: spec.viewport || null,
background: parsers.background(spec.background),
signals: parsers.signals(model, spec.signals),
predicates: parsers.predicates(model, spec.predicates),
marks: parsers.marks(model, spec, width, height),
data: parsers.data(model, spec.data, create)
});
try {
// protect against subsequent spec modification
spec = dl.duplicate(spec);

var parsers = require('./'),
width = spec.width || 500,
height = spec.height || 500,
padding = parsers.padding(spec.padding);

// create signals for width, height and padding
model.signal('width', width);
model.signal('height', height);
model.signal('padding', padding);

// initialize model
model.defs({
width: width,
height: height,
padding: padding,
viewport: spec.viewport || null,
background: parsers.background(spec.background),
signals: parsers.signals(model, spec.signals),
predicates: parsers.predicates(model, spec.predicates),
marks: parsers.marks(model, spec, width, height),
data: parsers.data(model, spec.data, onCreate)
});
} catch (err) { onError(err); }
}

if (dl.isObject(spec)) {
parse(spec);
} else if (dl.isString(spec)) {
var opts = dl.extend({url: spec}, model.config().load);
dl.load(opts, function(err, data) {
if (err) {
log.error('LOADING SPECIFICATION FAILED: ' + err.statusText);
} else {
try {
parse(JSON.parse(data));
} catch (e) {
log.error('INVALID SPECIFICATION: Must be a valid JSON object. '+e);
}
}
dl.json(opts, function(err, spec) {
if (err) onError('SPECIFICATION LOAD FAILED: ' + err);
else parse(spec);
});
} else {
log.error('INVALID SPECIFICATION: Must be a valid JSON object or URL.');
onError('INVALID SPECIFICATION: Must be a valid JSON object or URL.');
}
}

module.exports = parseSpec;

parseSpec.schema = {
"defs": {
"spec": {
Expand Down
9 changes: 7 additions & 2 deletions src/scene/GroupBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ proto.init = function(graph, def) {
};

proto.evaluate = function() {
var output = Builder.prototype.evaluate.apply(this, arguments),
var output = Builder.prototype.evaluate.apply(this, arguments),
model = this._graph,
builder = this;

output.add.forEach(function(group) { buildGroup.call(builder, output, group); });
output.rem.forEach(function(group) { model.group(group._id, null); });
return output;
};

Expand Down Expand Up @@ -206,6 +208,9 @@ function buildGroup(input, group) {

group.legends = group.legends || [];
group.legendItems = group.legendItems || [];

// Index group by ID to enable safe scoped scale lookups.
this._graph.group(group._id, group);
}

function buildMarks(input, group) {
Expand Down Expand Up @@ -266,4 +271,4 @@ function buildLegends(input, group) {
});
}

module.exports = GroupBuilder;
module.exports = GroupBuilder;
21 changes: 15 additions & 6 deletions test/_start.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
var Node = require('vega-dataflow/src/Node'),
var Node = require('vega-dataflow').Node,
log = require('vega-logging'),
chai = require('chai'),
spies = require('chai-spies');
spies = require('chai-spies'),
tv4 = require('tv4');

// configure logging
log.error = function() {}; // disable error output during tests

// set globals
global.d3 = require('d3');
global.dl = require('datalib');
global.chai = chai.use(spies);
global.expect = chai.expect;
global.transforms = require('../src/transforms/');
global.parseSpec = require('../src/parse/spec');
global.schema = require('../src/core/schema')();
global.tv4 = tv4;

var tv4 = global.tv4 = require('tv4');
global.validator = function(schema) {
return function(data) {
return tv4.validate(data, schema);
Expand All @@ -21,8 +27,11 @@ global.schemaPath = function(path) {
return dl.extend({ refs: schema.refs, defs: schema.defs }, path);
};

global.modelFactory = function(model) { return (model.fire(), model); };
global.viewFactory = function(model) {
global.modelFactory = function(model) {
return (model.fire(), model);
};

global.viewFactory = function(model) {
model.scene(new Node(model)).fire();
return model;
return model;
};
58 changes: 58 additions & 0 deletions test/parse/spec.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
describe('Spec Parser', function() {

it('should return error for null input', function(done) {
parseSpec(null, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for undefined input', function(done) {
parseSpec(undefined, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for boolean input', function(done) {
parseSpec(false, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for numeric input', function(done) {
parseSpec(1, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for invalid spec url', function(done) {
var spec = 'h!!p://12f.3z';
parseSpec(spec, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for invalid data url', function(done) {
var spec = {
data: [{name: 'table', url: 'h!!p://12f.3z'}]
};
parseSpec(spec, function(error, chart) {
expect(error).to.exist;
done();
})
});

it('should return error for invalid data format', function(done) {
var spec = {
data: [{name: 'table', values: '!#$%^&'}]
};
parseSpec(spec, function(error, chart) {
expect(error).to.exist;
done();
})
});
});
20 changes: 10 additions & 10 deletions test/parse/streams.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var dl = require('datalib'),

describe('Streams', function() {
function test(spec, interaction) {
parseSpec(spec, function(chart) {
parseSpec(spec, function(error, chart) {
jsdom.env("<html><body></body></html>", function(err, window) {
var document = window.document,
body = d3.select(document).select('body').node();
Expand All @@ -15,7 +15,7 @@ describe('Streams', function() {
.update();

interaction(view, d3.select(body).select('.marks').node(), mouseEvt);

function mouseEvt(type, x, y, target) {
var mm = document.createEvent("MouseEvents");
mm.initMouseEvent("mousemove", true, true, window, null, x, y, x, y, false, false, false, false, target);
Expand Down Expand Up @@ -155,7 +155,7 @@ describe('Streams', function() {
streams: [{ type: "mousedown", expr: "event" }]
}, {
name: "signalB",
streams: [{
streams: [{
type: "signalA", expr: "signalA.clientX",
scale: {name: "x", invert: true}
}]
Expand Down Expand Up @@ -205,7 +205,7 @@ describe('Streams', function() {
mouseEvt('mousedown', 201, 350, svg);
expect(view.signal('signalA')).to.not.be.undefined;
done();
});
});
});

it('should propagate ordered streams', function(done) {
Expand All @@ -227,8 +227,8 @@ describe('Streams', function() {
expect(view.signal('signalA')).to.have.property('clientX', 250);
expect(view.signal('signalA')).to.have.property('clientY', 350);

mouseEvt('mouseup', 250, 350, svg);
mouseEvt('mousemove', 190, 100, svg);
mouseEvt('mouseup', 250, 350, svg);
mouseEvt('mousemove', 190, 100, svg);
expect(view.signal('signalA')).to.have.property('clientX', 250);
expect(view.signal('signalA')).to.have.property('clientY', 350);

Expand Down Expand Up @@ -256,13 +256,13 @@ describe('Streams', function() {
expect(view.signal('signalA')).to.have.property('clientX', 190);
expect(view.signal('signalA')).to.have.property('clientY', 100);

mouseEvt('mouseup', 250, 350, svg);
mouseEvt('mousemove', 190, 100, svg);
mouseEvt('mouseup', 250, 350, svg);
mouseEvt('mousemove', 190, 100, svg);
expect(view.signal('signalA')).to.have.property('clientX', 250);
expect(view.signal('signalA')).to.have.property('clientY', 350);

done();
});
});
});

it('should populate vg events', function(done) {
Expand Down Expand Up @@ -354,7 +354,7 @@ describe('Streams', function() {
expect(sgA).to.have.deep.property("vg.name.mark2.mark.def.name", "mark2");
expect(sgA).to.have.deep.property("vg.name.mark2.x", 25);
expect(sgA).to.have.deep.property("vg.name.mark2.fill", "green");

expect(sgA).to.have.deep.property("vg.name.mark1.mark.marktype", "group");
expect(sgA).to.have.deep.property("vg.name.mark1.mark.def.name", "mark1");
expect(sgA).to.have.deep.property("vg.name.mark1.x", 25);
Expand Down
6 changes: 2 additions & 4 deletions test/render/canvas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ describe('Canvas', function() {
});
});

// Render the given spec using both the headless string renderer
// and the standard SVG renderer (in a fake JSDOM)
// and compare that the SVG output is identical
// Render the given spec using the headless canvas renderer
function render(name, specFile, done) {
fs.readFile(specFile, "utf8", function(err, text) {
if (err) throw err;
var spec = JSON.parse(text);

parseSpec(spec, function(viewFactory) {
parseSpec(spec, function(error, viewFactory) {
var view = viewFactory({ renderer: "canvas" }).update();
view.canvasAsync(function(canvas) {
var data = canvas.toDataURL();
Expand Down
4 changes: 2 additions & 2 deletions test/render/svg.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('SVG', function() {
if (err) throw err;
var spec = JSON.parse(text);

parseSpec(spec, function(viewFactory) {
parseSpec(spec, function(error, viewFactory) {
if (headless) {
var view = viewFactory({ renderer: "svg" }).update();
var svg = view.renderer().svg();
Expand All @@ -68,7 +68,7 @@ describe('SVG', function() {
} else {
jsdom.env("<html><body></body></html>", function(err, window) {
global.window = window;

var body = d3.select(window.document).select('body').node();
var view = viewFactory({ renderer: "svg", el: body }).update();
var svg = d3.select(body).select('div.vega').node().innerHTML
Expand Down
252 changes: 126 additions & 126 deletions test/scene/scale.test.js

Large diffs are not rendered by default.

57 changes: 28 additions & 29 deletions test/transforms/aggregate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ describe('Aggregate', function() {
"name": "table",
"values": values,
"transform": [{"type": "aggregate", "summarize": [{
"field": "y",
"field": "y",
"ops": ["count", "sum", "min", "max"]
}]}]
}]
};

it('should compute summaries', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
count = values.length,
Expand All @@ -43,17 +43,16 @@ describe('Aggregate', function() {
expect(data[0]).to.have.property('min_y', min);
expect(data[0]).to.have.property('max_y', max);


done();
}, modelFactory);
});
});

// Assume other measures are being tested in datalib.
it('should handle renamed output', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].summarize[0].as = ["a", "b", "c", "d"];

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
count = values.length,
Expand All @@ -69,11 +68,11 @@ describe('Aggregate', function() {
expect(data[0]).to.have.property('d', max);

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var a1 = {x: 21, y: 21},
a2 = {x: 22, y: 95},
a3 = {x: 23, y: 47};
Expand All @@ -96,14 +95,14 @@ describe('Aggregate', function() {
expect(data[0]).to.have.property('max_y', max);

done();
}, modelFactory);
});
});

it('should handle streaming mods', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
model.data('table')
.synchronize()
.update(function(d) { return d.x % 2 !== 0 }, "y",
.update(function(d) { return d.x % 2 !== 0 }, "y",
function(d) { return d.y * 2 })
.fire();

Expand All @@ -122,11 +121,11 @@ describe('Aggregate', function() {
expect(data[0]).to.have.property('max_y', max);

done();
}, modelFactory);
});
});

it('should handle streaming rems', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
values = values.filter(function(d) { return d.y < 50 });
model.data('table').synchronize()
.remove(function(d) { return d.y >= 50 }).fire();
Expand All @@ -146,7 +145,7 @@ describe('Aggregate', function() {
expect(data[0]).to.have.property('max_y', max);

done();
}, modelFactory);
});
});
});

Expand All @@ -169,7 +168,7 @@ describe('Aggregate', function() {
};

it('should calculate multiple aggregations', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values();

Expand All @@ -186,11 +185,11 @@ describe('Aggregate', function() {
expect(data[1]).to.have.property('sum_population', 6);

done();
}, modelFactory);
});
});

it('should handle modified keys', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table')
.synchronize()
.update(function(d) { return d.country === "Canada" },
Expand All @@ -210,15 +209,15 @@ describe('Aggregate', function() {
expect(data[1]).to.have.property('sum_population', 6);

done();
}, modelFactory);
});
});

it('should handle signals', function(done) {
var s = dl.duplicate(spec);
s.signals = [{"name": "field", "init": "area"}, {"name": "ops", "init": ["sum", "count"]}];
s.data[0].transform[0].summarize = [{"field": {"signal": "field"}, "ops": {"signal": "ops"}}];

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values();

Expand All @@ -243,10 +242,10 @@ describe('Aggregate', function() {

expect(data[1]).to.have.property('country', 'Canada');
expect(data[1]).to.have.property('sum_population', 6);
expect(data[1]).to.have.property('count_population', 2);
expect(data[1]).to.have.property('count_population', 2);

done();
}, modelFactory);
});
});
});

Expand All @@ -258,7 +257,7 @@ describe('Aggregate', function() {

expect(validate({ "type": "aggregate" })).to.be.true;
expect(validate({ "type": "aggregate", "groupby": ["country"] })).to.be.true;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": {
Expand All @@ -267,7 +266,7 @@ describe('Aggregate', function() {
}
})).to.be.true;

expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": [
Expand All @@ -276,7 +275,7 @@ describe('Aggregate', function() {
]
})).to.be.true;

expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": [
Expand All @@ -286,45 +285,45 @@ describe('Aggregate', function() {
})).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": "country",
"summarize": {
"medals": ["count", "min", "max"],
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": {
"medals": 1,
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": {
"medals": ["count", "min", "max", "foo"],
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": [
{"field": 1, "ops": ["argmin", "argmax"]}
]
})).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": [
{"field": "gdp", "ops": ["argmin", "argmax", "foo"]}
]
})).to.be.false;
expect(validate({
expect(validate({
"type": "aggregate",
"groupby": ["country"],
"summarize": [
Expand Down
54 changes: 27 additions & 27 deletions test/transforms/bin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ describe('Bin', function() {
8.7, 8.8, 8.9,
9.1, 9.2, 9.3
];

function spec(opt) {
var bin = {"type": "bin", "field": "v", "output": {"start": "bin_v"}};
for (var name in opt) bin[name] = opt[name];
return {
"data": [{
"name": "table",
return {
"data": [{
"name": "table",
"values": values.map(function(x) { return {v:x}; }),
"transform": [bin]
}]
}]
};
}

it('should handle extent calculation', function(done) {
parseSpec(spec({maxbins: 10}), function(model) {
parseSpec(spec({maxbins: 10}), modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
floored = values.map(function(x) { return ~~x; });
Expand All @@ -35,13 +35,13 @@ describe('Bin', function() {
for (var i=0, len=data.length; i<len; ++i) {
expect(data[i].bin_v).to.equal(floored[i]);
}

done();
}, modelFactory);
});
});

it('should handle step definition', function(done) {
parseSpec(spec({min:0, max:10, step:1}), function(model) {
parseSpec(spec({min:0, max:10, step:1}), modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
floored = values.map(function(x) { return ~~x; });
Expand All @@ -52,13 +52,13 @@ describe('Bin', function() {
expect(data[i].bin_end).to.equal(floored[i]+1);
expect(data[i].bin_mid).to.equal(floored[i]+0.5);
}

done();
}, modelFactory);
});
});

it('should handle maxbins definition', function(done) {
parseSpec(spec({min:0, max:10, maxbins: 5}), function(model) {
parseSpec(spec({min:0, max:10, maxbins: 5}), modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
floored = values.map(function(x) { return ~~x - (~~x % 2); });
Expand All @@ -67,16 +67,16 @@ describe('Bin', function() {
for (var i=0, len=data.length; i<len; ++i) {
expect(data[i].bin_v).to.equal(floored[i]);
}

done();
}, modelFactory);
});
});

it('should handle nulls', function(done) {
parseSpec(spec({min:0, max:10, step: 1}), function(model) {
parseSpec(spec({min:0, max:10, step: 1}), modelFactory, function(error, model) {
var ds = model.data('table').insert([{v: null}, {v: undefined}]);
model.fire();

var data = ds.values(),
floored = values.map(function(x) { return ~~x; });
floored.push(null, null);
Expand All @@ -85,19 +85,19 @@ describe('Bin', function() {
for (var i=0, len=data.length; i<len; ++i) {
expect(data[i].bin_v).to.equal(floored[i]);
}

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec({min:0, max:10, step: 2}), function(model) {
parseSpec(spec({min:0, max:10, step: 2}), modelFactory, function(error, model) {
var ds = model.data('table')
.insert([{v:1.1}])
.insert([{v:-2.1}])
.insert([{v:11.2}]);
ds.fire();

var data = ds.values(),
floored = values.map(function(x) { return ~~x - (~~x%2); });
floored.push(0, -2, 10);
Expand All @@ -106,30 +106,30 @@ describe('Bin', function() {
for (var i=0, len=data.length; i<len; ++i) {
expect(data[i].bin_v).to.equal(floored[i]);
}

done();
}, modelFactory);
});
});

it('should handle streaming mods', function(done) {
parseSpec(spec({min:0, max:10, step: 1}), function(model) {
parseSpec(spec({min:0, max:10, step: 1}), modelFactory, function(error, model) {
var ds = model.data('table').update(
function(d) { return d.v < 2; },
"v",
function(d) { return Math.random(); }
);
ds.fire();

var data = ds.values(),
floored = values.map(function(x) { return x < 2 ? 0 : ~~x; });

expect(data.length).to.be.above(0).and.equal(floored.length);
for (var i=0, len=data.length; i<len; ++i) {
expect(data[i].bin_v).to.equal(floored[i]);
}

done();
}, modelFactory);
});
});

it('should validate against the schema', function() {
Expand Down
12 changes: 6 additions & 6 deletions test/transforms/countpattern.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('CountPattern', function() {
};

it('should count patterns', function(done) {
parseSpec(spec1, function(model) {
parseSpec(spec1, modelFactory, function(error, model) {
var data = model.data('table').values().sort(dl.comparator('-count'));
expect(data.length).to.equal(3);
expect(data[0].text).to.equal('a');
Expand All @@ -41,16 +41,16 @@ describe('CountPattern', function() {
expect(data[0].count).to.equal(4);
expect(data[1].count).to.equal(3);
expect(data[2].count).to.equal(1);
parseSpec(spec2, function(model) {

parseSpec(spec2, modelFactory, function(error, model) {
var data = model.data('table').values().sort(dl.comparator('-count'));
expect(data.length).to.equal(1);
expect(data[0].text).to.equal(' ');
expect(data[0].count).to.equal(15);
done();
}, modelFactory);
});

}, modelFactory);
});
});

it('should validate against the schema', function() {
Expand All @@ -70,7 +70,7 @@ describe('CountPattern', function() {
expect(validate({ "type": "countpattern", "stopwords": {"signal": "stop"} })).to.be.true;
expect(validate({ "type": "countpattern", "output": {"text": "text"} })).to.be.true;
expect(validate({ "type": "countpattern", "output": {"count": "count"} })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "countpattern", "field": 1 })).to.be.false;
expect(validate({ "type": "countpattern", "pattern": 2 })).to.be.false;
Expand Down
94 changes: 47 additions & 47 deletions test/transforms/cross.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ describe('Cross', function() {
{"x": 1, "y": 28}, {"x": 2, "y": 55},
{"x": 3, "y": 43}]
var values2 = [
{"x": 4, "y": 91}, {"x": 5, "y": 81},
{"x": 4, "y": 91}, {"x": 5, "y": 81},
{"x": 6, "y": 53}]

var spec = {
Expand All @@ -22,7 +22,7 @@ describe('Cross', function() {
};

it('should handle initial datasource', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
ds2 = model.data('table2'),
data1 = ds1.values(),
Expand All @@ -35,14 +35,14 @@ describe('Cross', function() {
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
ds2 = model.data('table2'),
new1 = {"x": 7, "y": 19},
new1 = {"x": 7, "y": 19},
new2 = {"x": 8, "y": 87},
data1 = ds1.values(),
data2 = ds2.values();
Expand All @@ -67,12 +67,12 @@ describe('Cross', function() {
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

done();
}, modelFactory);
});

});

it('should handle streaming rems', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
ds2 = model.data('table2'),
data1 = ds1.values(),
Expand All @@ -84,22 +84,22 @@ describe('Cross', function() {

ds1.remove(function(x) { return x.x == 1; }).fire();
data1 = ds1.values();
data2 = ds2.values();
data2 = ds2.values();

expect(data1).to.have.length(2);
expect(data2).to.have.length(6);
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

ds2.remove(function(x) { return x.x == 4; }).fire();
data1 = ds1.values();
data2 = ds2.values();
data2 = ds2.values();

expect(data1).to.have.length(2);
expect(data2).to.have.length(4);
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

// Test that lazy removal is working correctly.
ds2.update(function(x) { return x.x == 6; },
ds2.update(function(x) { return x.x == 6; },
'y', function(x) { return 600; }).fire();
data1 = ds1.values(),
data2 = ds2.values();
Expand All @@ -109,12 +109,12 @@ describe('Cross', function() {
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

done();
}, modelFactory);
});

});

it('should propegate mod tuples', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
ds2 = model.data('table2'),
data1 = ds1.values(),
Expand All @@ -124,7 +124,7 @@ describe('Cross', function() {
expect(data2).to.have.length(9);
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

ds2.update(function(x) { return x.x == 1; },
ds2.update(function(x) { return x.x == 1; },
'y', function(x) { return 100; }).fire();
data1 = ds1.values(),
data2 = ds2.values();
Expand All @@ -133,7 +133,7 @@ describe('Cross', function() {
expect(data2).to.have.length(9);
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

ds2.update(function(x) { return x.x == 4; },
ds2.update(function(x) { return x.x == 4; },
'y', function(x) { return 400; }).fire();
data1 = ds1.values(),
data2 = ds2.values();
Expand All @@ -143,15 +143,15 @@ describe('Cross', function() {
expect(ds2._output.fields).to.contain.keys(['a', 'b']);

done();
}, modelFactory);
});

});

it('should allow renamed keys', function(done) {
var s = dl.duplicate(spec);
s.data[1].transform[0].output = {"left": "thing1", "right": "thing2"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
ds2 = model.data('table2'),
data1 = ds1.values(),
Expand All @@ -162,23 +162,23 @@ describe('Cross', function() {
expect(ds2._output.fields).to.contain.keys(['thing1', 'thing2']);

done();
}, modelFactory);
});

});

it('should self cross', function(done) {
var spec = {
"data": [{
"name": "table1",
"name": "table1",
"values": values1,
"transform": [{"type": "cross"}]
}]
};

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
data1 = ds1.values(),
new1 = {"x": 7, "y": 19},
new1 = {"x": 7, "y": 19},
new2 = {"x": 8, "y": 87};

expect(data1).to.have.length(9);
Expand All @@ -190,54 +190,54 @@ describe('Cross', function() {
ds1.update(function() { return true; }, 'x', function(t) { return t.x+1; })
.fire();
data1 = ds1.values();
expect(data1).to.have.length(25);
expect(data1).to.have.length(25);

ds1.remove(function(t) { return t.x === 8; }).fire();
data1 = ds1.values();
expect(data1).to.have.length(16);

done();
}, modelFactory);
});
});

it('should exclude diagonal values', function(done) {
var spec = {
"data": [{
"name": "table1",
"name": "table1",
"values": values1,
"transform": [{"type": "cross", "diagonal": false}]
}]
};

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
data1 = ds1.values();

expect(data1).to.have.length(6);

done();
}, modelFactory);
});
});

it('should exclude filtered values', function(done) {
var spec = {
"data": [{
"name": "table1",
"name": "table1",
"values": values1,
"transform": [{"type": "cross", "filter": "datum.a.x >= 2 && datum.b.y < 40"}]
}]
};

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
data1 = ds1.values(),
new1 = {"x": 7, "y": 19},
new1 = {"x": 7, "y": 19},
new2 = {"x": 8, "y": 87};

expect(data1).to.have.length(2);

ds1.insert([new1, new2]).fire();
data1 = ds1.values();
data1 = ds1.values();
expect(data1).to.have.length(8);

ds1.update(function(t) { return t.x === 3; }, 'y', function(t) { return 39; })
Expand All @@ -248,14 +248,14 @@ describe('Cross', function() {
ds1.update(function(t) { return t.x === 3; }, 'y', function(t) { return 41; })
.fire();
data1 = ds1.values();
expect(data1).to.have.length(8);
expect(data1).to.have.length(8);

ds1.remove(function(t) { return t.x >=7; }).fire();
data1 = ds1.values();
expect(data1).to.have.length(2);

done();
}, modelFactory);
});
});

it('should recross on signal change', function(done) {
Expand All @@ -275,7 +275,7 @@ describe('Cross', function() {
}]
};

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds1 = model.data('table1'),
data1 = ds1.values(),
out1 = ds1._output;
Expand All @@ -298,11 +298,11 @@ describe('Cross', function() {
out1 = ds1._output;
expect(data1).to.have.length(2);
expect(out1.rem).to.have.length(4);
expect(out1.add).to.have.length(2);
expect(out1.mod).to.have.length(0);
expect(out1.add).to.have.length(2);
expect(out1.mod).to.have.length(0);

done();
}, modelFactory);
});
});

it('should validate against the schema', function() {
Expand All @@ -312,27 +312,27 @@ describe('Cross', function() {
expect(validate({ "type": "cross" })).to.be.true;
expect(validate({ "type": "cross", "with": "table" })).to.be.true;
expect(validate({ "type": "cross", "with": "table", "diagonal": false })).to.be.true;
expect(validate({
"type": "cross",
"with": "table",
"output": {"left": "foo", "right": "bar"}
expect(validate({
"type": "cross",
"with": "table",
"output": {"left": "foo", "right": "bar"}
})).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "cross", "with": 5 })).to.be.false;
expect(validate({ "type": "cross", "with": "table", "diagonal": 1 })).to.be.false;
expect(validate({
"type": "cross",
"with": "table",
"output": {"left": 1, "right": 2}
expect(validate({
"type": "cross",
"with": "table",
"output": {"left": 1, "right": 2}
})).to.be.false;

expect(validate({
"type": "cross",
"with": "table",
expect(validate({
"type": "cross",
"with": "table",
"output": {"left": "foo", "right": "bar"},
"hello": "world"
})).to.be.false;
});

});
58 changes: 29 additions & 29 deletions test/transforms/facet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,23 @@ describe('Facet', function() {
}

it('should handle initial datasource', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
facets = ds.values(),
facets = ds.values(),
i, len;

expect(facets).to.have.length(2);
expectFacet(facets, 0, 0, 2); // USA
expectFacet(facets, 1, 3, 5); // Canada

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
facets = ds.values(),
facets = ds.values(),
i, len;

expect(facets).to.have.length(2);
Expand All @@ -67,11 +67,11 @@ describe('Facet', function() {
expectFacet(facets, 2, 8, 8); // Mexico

done();
}, modelFactory);
});
});

it('should handle streaming mods', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table').synchronize(),
facets = ds.values(),
i, len;
Expand All @@ -96,7 +96,7 @@ describe('Facet', function() {

// Changing key field
values[8].country = "India";
ds.update(function(x) { return x.country === "Mexico" },
ds.update(function(x) { return x.country === "Mexico" },
"country", function(x) { return "India"; }).fire();
facets = ds.values();
expect(facets).to.have.length(3);
Expand All @@ -105,13 +105,13 @@ describe('Facet', function() {
expectFacet(facets, 2, 8, 8); // India

done();
}, modelFactory);
});
});

it('should handle streaming rems', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table').synchronize(),
facets = ds.values(),
facets = ds.values(),
i, len;

expect(facets).to.have.length(3);
Expand All @@ -129,16 +129,16 @@ describe('Facet', function() {
expectFacet(facets, 1, 3, 5); // Canada

done();
}, modelFactory);
});
})

it('should handle signals as keys', function(done) {
var s = dl.duplicate(spec);
spec.data[0].transform[0].groupby = [{"signal": "keys"}];

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
facets = ds.values(),
facets = ds.values(),
i, len;

expect(facets).to.have.length(2);
Expand All @@ -158,17 +158,17 @@ describe('Facet', function() {
expect(facets[2].values).to.have.length(2);

done();
}, modelFactory);
});
});

it('should handle fields+signals as keys', function(done) {
var s = dl.duplicate(spec);
spec.signals[0].init = 'type';
spec.data[0].transform[0].groupby = [{"field": "country"}, {"signal": "keys"}];

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
facets = ds.values(),
facets = ds.values(),
i, len;

expect(facets).to.have.length(6);
Expand All @@ -187,7 +187,7 @@ describe('Facet', function() {
expect(facets[5].values).to.have.length(1);

done();
}, modelFactory);
});
});

it('should compute summaries on facets', function(done) {
Expand All @@ -205,7 +205,7 @@ describe('Facet', function() {
}]
};

parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values();

Expand All @@ -224,7 +224,7 @@ describe('Facet', function() {
expect(data[1]).to.have.property('max_count', 5);

done();
}, modelFactory);
});
});

it('should transform faceted values');
Expand All @@ -236,7 +236,7 @@ describe('Facet', function() {
expect(validate({ "type": "facet" })).to.be.true;
expect(validate({ "type": "facet", "groupby": ["country"] })).to.be.true;

expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": {
Expand All @@ -245,7 +245,7 @@ describe('Facet', function() {
}
})).to.be.true;

expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": [
Expand All @@ -254,7 +254,7 @@ describe('Facet', function() {
]
})).to.be.true;

expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": [
Expand All @@ -264,45 +264,45 @@ describe('Facet', function() {
})).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": "country",
"summarize": {
"medals": ["count", "min", "max"],
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": {
"medals": 1,
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": {
"medals": ["count", "min", "max", "foo"],
"gdp": ["argmin", "argmax"]
}
})).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": [
{"field": 1, "ops": ["argmin", "argmax"]}
]
})).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": [
{"field": "gdp", "ops": ["argmin", "argmax", "foo"]}
]
})).to.be.false;
expect(validate({
expect(validate({
"type": "facet",
"groupby": ["country"],
"summarize": [
Expand Down
34 changes: 17 additions & 17 deletions test/transforms/filter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,38 @@ describe('Filter', function() {
];

it('should work w/a static expr', function(done) {
parseSpec({
"data": [{
"name": "table",
parseSpec({
"data": [{
"name": "table",
"values": values,
"transform": [{"type": "filter", "test": "datum.y > 45"}]
}]
}, function(model) {
}]
}, viewFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
filtered = values.filter(function(d) { return d.y > 45 }),
data = ds.values(),
filtered = values.filter(function(d) { return d.y > 45 }),
i, len;

expect(data.length).to.be.above(0).and.equal(filtered.length);
for(i=0, len=data.length; i<len; ++i) expect(data[i].y).to.be.above(45);

done();
}, viewFactory);
});
});

it('should work w/signals in expr', function(done) {
parseSpec({
parseSpec({
"signals":[{"name": "above", "init": 45}],

"data": [{
"name": "table",
"data": [{
"name": "table",
"values": values,
"transform": [{"type": "filter", "test": "datum.y > above"}]
}]
}, function(model) {
}]
}, viewFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
filtered = values.filter(function(d) { return d.y > 45 }),
data = ds.values(),
filtered = values.filter(function(d) { return d.y > 45 }),
i, len;

expect(data.length).to.be.above(0).and.equal(filtered.length);
Expand All @@ -63,15 +63,15 @@ describe('Filter', function() {
for(i=0, len=data.length; i<len; ++i) expect(data[i].y).to.be.above(30);

done();
}, viewFactory);
});
});

it('should validate against the schema', function() {
var schema = schemaPath(transforms.filter.schema),
validate = validator(schema);

expect(validate({ "type": "filter", "test": "d.x > 5" })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "filter" })).to.be.false;
expect(validate({ "type": "filter", "test": true })).to.be.false;
Expand Down
66 changes: 33 additions & 33 deletions test/transforms/fold.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ describe('Fold', function() {
];

var spec = {
"data": [{
"name": "table",
"data": [{
"name": "table",
"values": values,
"transform": [{
"type": "fold",
"type": "fold",
"fields": [{"field": "gold"}, {"field": "silver"}, {"field": "bronze"}]
}]
}]
}]
};

function expectFold(val, data, idx, key, value) {
Expand All @@ -35,9 +35,9 @@ describe('Fold', function() {


it('should handle initial datasource', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -46,15 +46,15 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['key', 'value']);

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
mex = {"country": "Mexico", "gold": 3, "silver": 3, "bronze": 2},
bel = {"country": "Belize", "gold": 0, "silver": 0, "bronze": 0},
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -72,13 +72,13 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['key', 'value']);

done();
}, modelFactory);
});
});

it('should handle streaming rems', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -93,21 +93,21 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['key', 'value']);

done();
}, modelFactory);
});
});

it('should propagate mod tuples if fields updated', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
expectFold(values[0], data); // USA
expectFold(values[1], data); // Canada
expect(ds._output.fields).to.have.keys(['key', 'value']);

ds.update(function(x) { return x.country == "US" },
ds.update(function(x) { return x.country == "US" },
'gold', function(x) { return 100; }).fire();
data = ds.values();
expect(data).to.have.length(6);
Expand All @@ -128,13 +128,13 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['gold', 'key', 'value']);

done();
}, modelFactory);
});
});

it('should only propagate mod tuples if fields not updated', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -150,16 +150,16 @@ describe('Fold', function() {
expect(ds._output.fields).to.not.have.keys(['key', 'value']);

done();
}, modelFactory);
});
});

it('should allow renamed keys', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].output = {key: "type"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -168,16 +168,16 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['type', 'value']);

done();
}, modelFactory);
});
});

it('should allow renamed values', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].output = {value: "medals"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(6);
Expand All @@ -186,7 +186,7 @@ describe('Fold', function() {
expect(ds._output.fields).to.have.keys(['key', 'medals']);

done();
}, modelFactory);
});
});

it('should allow array<signal> for fields?');
Expand All @@ -196,19 +196,19 @@ describe('Fold', function() {
validate = validator(schema);

expect(validate({ "type": "fold", "fields": ["gold", "silver"] })).to.be.true;
expect(validate({
"type": "fold",
"fields": ["gold", "silver"],
"output": {"key": "k", "value": "v"}
expect(validate({
"type": "fold",
"fields": ["gold", "silver"],
"output": {"key": "k", "value": "v"}
})).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "fold" })).to.be.false;
expect(validate({ "type": "fold", "foo": "bar" })).to.be.false;
expect(validate({ "type": "fold", "fields": "gold" })).to.be.false;
expect(validate({ "type": "fold", "fields": ["gold", 1] })).to.be.false;
expect(validate({
"type": "fold",
expect(validate({
"type": "fold",
"fields": ["gold"],
"output": {"foo": "bar"}
})).to.be.false;
Expand Down
18 changes: 8 additions & 10 deletions test/transforms/force.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ describe('Force', function() {
}

it('should perform layout', function(done) {
parseSpec(spec({}),
function(model) {
parseSpec(spec({}), modelFactory,
function(error, model) {
var nodes = model.data('vertices').values(),
links = model.data('edges').values();

Expand All @@ -51,16 +51,15 @@ describe('Force', function() {
}

done();
},
modelFactory);
});
});

it('should respect link distances', function(done) {
var linkDistances = [20, 100, 200];

linkDistances.forEach(function(dist, i) {
parseSpec(spec({linkDistance: dist, iterations: 100}),
function(model) {
parseSpec(spec({linkDistance: dist, iterations: 100}), modelFactory,
function(error, model) {
var nodes = model.data('vertices').values(),
links = model.data('edges').values();

Expand All @@ -75,8 +74,7 @@ describe('Force', function() {
}

if (i == linkDistances.length - 1) done();
},
modelFactory);
});
});
});

Expand All @@ -99,7 +97,7 @@ describe('Force', function() {
expect(validate({ "type": "force", "links": "edges", "gravity": 0.4 })).to.be.true;
expect(validate({ "type": "force", "links": "edges", "alpha": 0.4 })).to.be.true;
expect(validate({ "type": "force", "links": "edges", "output": {"x": "x", "y": "y"} })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "force" })).to.be.false;
expect(validate({ "type": "force", "links": true })).to.be.false;
Expand Down
30 changes: 15 additions & 15 deletions test/transforms/formula.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ describe('Formula', function() {
];

it('should work w/a static expr', function(done) {
parseSpec({
"data": [{
"name": "table",
parseSpec({
"data": [{
"name": "table",
"values": values,
"transform": [{"type": "formula", "field": "z", "expr": "datum.x + datum.y"}]
}]
}, function(model) {
}]
}, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -32,21 +32,21 @@ describe('Formula', function() {
expect(ds._output.fields).to.have.key('z');

done();
}, modelFactory);
});
});

it('should work w/signals in expr', function(done) {
parseSpec({
parseSpec({
"signals":[{"name": "multipler", "init": 2}],

"data": [{
"name": "table",
"data": [{
"name": "table",
"values": values,
"transform": [{"type": "formula", "field": "z", "expr": "multipler * (datum.x + datum.y)"}]
}]
}, function(model) {
}]
}, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand Down Expand Up @@ -75,15 +75,15 @@ describe('Formula', function() {
expect(ds._output.fields).to.have.key('z');

done();
}, modelFactory);
});
});

it('should validate against the schema', function() {
var schema = schemaPath(transforms.formula.schema),
validate = validator(schema);

expect(validate({ "type": "formula", "expr": "d.x + d.y", "field": "sum" })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "formula" })).to.be.false;
expect(validate({ "type": "formula", "field": "sum" })).to.be.false;
Expand Down
7 changes: 3 additions & 4 deletions test/transforms/hierarchy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ describe('Hierarchy', function() {
}

it('should perform hierarchy layout', function(done) {
parseSpec(spec(),
function(model) {
parseSpec(spec(), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values();

Expand All @@ -35,8 +35,7 @@ describe('Hierarchy', function() {
});

done();
},
modelFactory);
});
});

it('should validate against the schema', function() {
Expand Down
43 changes: 19 additions & 24 deletions test/transforms/impute.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('Impute', function() {
{a: 2, b: 3, c: 5},
{a: 2, b: 4, c: 11}
];

function spec(opt) {
var impute = {
type: 'impute',
Expand All @@ -18,7 +18,7 @@ describe('Impute', function() {
method: opt.method,
value: opt.value
};

return {
data: [{
name: "table",
Expand All @@ -33,8 +33,8 @@ describe('Impute', function() {
};

it('should impute values', function(done) {
parseSpec(spec({method: 'value', value: -1}),
function(model) {
parseSpec(spec({method: 'value', value: -1}), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values().filter(imputed);

Expand All @@ -46,64 +46,59 @@ describe('Impute', function() {
expect(data[1]).to.have.property('b', 2);
expect(data[1]).to.have.property('c', -1);
done();
},
modelFactory);
});
});

it('should impute mean', function(done) {
parseSpec(spec({method: 'mean'}),
function(model) {
parseSpec(spec({method: 'mean'}), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values().filter(imputed);

expect(data).to.have.length(2);
expect(data[0]).to.have.property('c', 3);
expect(data[1]).to.have.property('c', 6);
done();
},
modelFactory);
});
});

it('should impute median', function(done) {
parseSpec(spec({method: 'median'}),
function(model) {
parseSpec(spec({method: 'median'}), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values().filter(imputed);

expect(data).to.have.length(2);
expect(data[0]).to.have.property('c', 3);
expect(data[1]).to.have.property('c', 5);
done();
},
modelFactory);
});
});

it('should impute min', function(done) {
parseSpec(spec({method: 'min'}),
function(model) {
parseSpec(spec({method: 'min'}), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values().filter(imputed);

expect(data).to.have.length(2);
expect(data[0]).to.have.property('c', 1);
expect(data[1]).to.have.property('c', 2);
done();
},
modelFactory);
});
});

it('should impute max', function(done) {
parseSpec(spec({method: 'max'}),
function(model) {
parseSpec(spec({method: 'max'}), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values().filter(imputed);

expect(data).to.have.length(2);
expect(data[0]).to.have.property('c', 5);
expect(data[1]).to.have.property('c', 11);
done();
},
modelFactory);
});
});

it('should validate against the schema', function() {
Expand All @@ -119,9 +114,9 @@ describe('Impute', function() {
expect(validate({ "type": "impute", "groupby": ["a"], "orderby": ["b"], "field": "c", "method": "value", "value": 5 })).to.be.true;
expect(validate({ "type": "impute", "groupby": ["a"], "orderby": ["b"], "field": "c", "method": "value", "value": "na" })).to.be.true;
expect(validate({ "type": "impute", "groupby": ["a"], "orderby": ["b"], "field": "c", "method": "value", "value": false })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "impute" })).to.be.false;
expect(validate({ "type": "impute" })).to.be.false;
expect(validate({ "type": "impute", "groupby": ["a"] })).to.be.false;
expect(validate({ "type": "impute", "groupby": ["a"], "orderby": "b" })).to.be.false;
expect(validate({ "type": "impute", "groupby": ["a"], "orderby": ["b"] })).to.be.false;
Expand Down
118 changes: 59 additions & 59 deletions test/transforms/lookup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ describe('Lookup', function() {
];

var spec = {
"data": [{
"name": "stats",
"data": [{
"name": "stats",
"values": statsVals
}, {
"name": "medals",
"values": medalsVals,
"transform": [
{
"type": "lookup",
"on": "stats",
"onKey": "country",
"keys": ["country"],
"type": "lookup",
"on": "stats",
"onKey": "country",
"keys": ["country"],
"as": ["country_stats"]
}
]
}]
}]
};

function expectUSA(medals) {
Expand All @@ -41,28 +41,28 @@ describe('Lookup', function() {
}

it('should handle initial datasources', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
expect(medals).to.have.length(2);
expectUSA(medals);
expectCanada(medals);
expectCanada(medals);

done();
}, modelFactory);
});
});

it('should handle streaming adds w/o default', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
Expand All @@ -86,26 +86,26 @@ describe('Lookup', function() {
expect(stats).to.have.length(3);
expect(medals).to.have.length(3);
expectUSA(medals);
expectCanada(medals);
expectCanada(medals);

expect(medals[2].country_stats).to.not.be.undefined;
expect(medals).to.have.deep.property('[2].country_stats.gdp', 1177);
expect(medals).to.have.deep.property('[2].country_stats.pop', 120);
expect(medals).to.have.deep.property('[2].country_stats.athletes', 78);

done();
}, modelFactory);
});
});

it('should handle streaming adds w/default', function(done) {
var s = dl.duplicate(spec);
s.data[1].transform[0].default = {"foo": "bar"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
Expand All @@ -130,23 +130,23 @@ describe('Lookup', function() {
expect(stats).to.have.length(3);
expect(medals).to.have.length(3);
expectUSA(medals);
expectCanada(medals);
expectCanada(medals);

expect(medals[2].country_stats).to.not.be.undefined;
expect(medals).to.have.deep.property('[2].country_stats.gdp', 1177);
expect(medals).to.have.deep.property('[2].country_stats.pop', 120);
expect(medals).to.have.deep.property('[2].country_stats.athletes', 78);

done();
}, modelFactory);
});
});

it('should handle streaming rems w/o default', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
Expand All @@ -155,7 +155,7 @@ describe('Lookup', function() {
expectCanada(medals);

statsDS.remove(function(x) { return x.country == "Canada" }).fire();
stats = statsDS.values();
stats = statsDS.values();
medals = medalsDS.values();
expect(stats).to.have.length(1);
expect(medals).to.have.length(2);
Expand All @@ -164,18 +164,18 @@ describe('Lookup', function() {
expect(medals[1].country_stats).to.be.undefined;

done();
}, modelFactory);
});
});

it('should handle streaming rems w/default', function(done) {
var s = dl.duplicate(spec);
s.data[1].transform[0].default = {"foo": "bar"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
Expand All @@ -184,7 +184,7 @@ describe('Lookup', function() {
expectCanada(medals);

statsDS.remove(function(x) { return x.country == "Canada" }).fire();
stats = statsDS.values();
stats = statsDS.values();
medals = medalsDS.values();
expect(stats).to.have.length(1);
expect(medals).to.have.length(2);
Expand All @@ -194,15 +194,15 @@ describe('Lookup', function() {
expect(medals).to.have.deep.property('[1].country_stats.foo', 'bar');

done();
}, modelFactory);
});
});

it('should propagate streaming mods', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
statsDS = model.data('stats'),
medals = medalsDS.values(),
stats = statsDS.values(),
medals = medalsDS.values(),
stats = statsDS.values(),
i, len, d;

expect(stats).to.have.length(2);
Expand All @@ -213,7 +213,7 @@ describe('Lookup', function() {
// Inner mod
statsDS.update(function(x) { return x.country == "Canada" },
'athletes', function(x) { return 100; }).fire();
stats = statsDS.values();
stats = statsDS.values();
medals = medalsDS.values();
expect(stats).to.have.length(2);
expect(medals).to.have.length(2);
Expand All @@ -226,7 +226,7 @@ describe('Lookup', function() {
// Key mod on joined datasource
statsDS.update(function(x) { return x.country == "Canada" },
'country', function(x) { return 'Mexico'; }).fire();
stats = statsDS.values();
stats = statsDS.values();
medals = medalsDS.values();
expect(stats).to.have.length(2);
expect(medals).to.have.length(2);
Expand All @@ -236,15 +236,15 @@ describe('Lookup', function() {
// Key mod on primary datasource
medalsDS.update(function(x) { return x.country == "Canada" },
'country', function(x) { return 'Mexico'; }).fire();
stats = statsDS.values();
stats = statsDS.values();
medals = medalsDS.values();
expect(stats).to.have.length(2);
expect(medals).to.have.length(2);
expectUSA(medals);
expect(medals[1].country_stats).to.not.be.undefined;

done();
}, modelFactory);
});
});

});
Expand All @@ -256,49 +256,49 @@ describe('Lookup', function() {
], zipVals = [{"zip2": "A"}, {"zip2": "B"}, {"zip2": "C"}];

var spec = {
"data": [{
"name": "zip",
"data": [{
"name": "zip",
"values": zipVals
}, {
"name": "medals",
"values": medalsVals,
"transform": [
{
"type": "lookup",
"type": "lookup",
"on": "zip",
"keys": ["index"],
"as": ["zip"]
}
]
}]
}]
};

it('should handle initial datasources', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
zipDS = model.data('zip'),
medals = medalsDS.values(),
zip = zipDS.values(),
zip = zipDS.values(),
i, len, d;

model.fire();
zip = zipDS.values();
zip = zipDS.values();
medals = medalsDS.values();
expect(zip).to.have.length(3);
expect(medals).to.have.length(2);
expect(medals).to.have.deep.property('[0].zip.zip2', 'A');
expect(medals).to.have.deep.property('[1].zip.zip2', 'B');

done();
}, modelFactory);
});
});

it('should handle streaming adds', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
zipDS = model.data('zip'),
medals = medalsDS.values(),
zip = zipDS.values(),
zip = zipDS.values(),
i, len, d;

expect(zip).to.have.length(3);
Expand All @@ -325,15 +325,15 @@ describe('Lookup', function() {
expect(medals).to.have.deep.property('[2].zip.zip2', 'C');

done();
}, modelFactory);
});
});

it('should handle streaming rems', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
zipDS = model.data('zip'),
medals = medalsDS.values(),
zip = zipDS.values(),
zip = zipDS.values(),
i, len, d;

expect(zip).to.have.length(3);
Expand All @@ -342,23 +342,23 @@ describe('Lookup', function() {
expect(medals).to.have.deep.property('[1].zip.zip2', 'B');

zipDS.remove(function(x) { return x.zip2 == "B" }).fire();
zip = zipDS.values();
zip = zipDS.values();
medals = medalsDS.values();
expect(zip).to.have.length(2);
expect(medals).to.have.length(2);
expect(medals).to.have.deep.property('[0].zip.zip2', 'A');
expect(medals).to.have.deep.property('[1].zip.zip2', 'C');

done();
}, modelFactory);
});
});

it('should propagate streaming mods', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var medalsDS = model.data('medals'),
zipDS = model.data('zip'),
medals = medalsDS.values(),
zip = zipDS.values(),
zip = zipDS.values(),
i, len, d;

expect(zip).to.have.length(3);
Expand All @@ -368,15 +368,15 @@ describe('Lookup', function() {

zipDS.update(function(x) { return x.zip2 == "B" },
'zip2', function(x) { return "F"; }).fire();
zip = zipDS.values();
zip = zipDS.values();
medals = medalsDS.values();
expect(zip).to.have.length(3);
expect(medals).to.have.length(2);
expect(medals).to.have.deep.property('[0].zip.zip2', 'A');
expect(medals).to.have.deep.property('[1].zip.zip2', 'F');

done();
}, modelFactory);
});
});

});
Expand All @@ -391,7 +391,7 @@ describe('Lookup', function() {
"as": ["t"], "keys": ["k"] })).to.be.true;
expect(validate({ "type": "lookup", "on": "table", "onKey": "k",
"as": ["t"], "keys": ["k"], "default": 1 })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "lookup" })).to.be.false;
expect(validate({ "type": "lookup", "on": "table" })).to.be.false;
Expand Down
48 changes: 24 additions & 24 deletions test/transforms/sort.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ describe('Sort', function() {
}

it('should sort asc w/a single static fieldName', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -37,16 +37,16 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});

it('should sort desc w/a single static fieldName', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].by.field = "-y";

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -55,16 +55,16 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});

it('should sort w/a single signal', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].by = {"signal": "sortBy1"};

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -80,16 +80,16 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});
});

it('should sort w/multiple static fieldNames', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].by = [{"field": "-x"}, {"field": "y"}];

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -101,16 +101,16 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});
});

it('should sort w/multiple signals', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].by = [{"signal": "sortBy0"}, {"signal": "sortBy1"}];

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand Down Expand Up @@ -142,16 +142,16 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});
});

it('should sort w/mixed fieldNames+signals', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].by = [{"field": "-x"}, {"signal": "sortBy1"}];

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var ds = model.data('table'),
data = ds.values(),
data = ds.values(),
i, len, d;

expect(data).to.have.length(20);
Expand All @@ -173,8 +173,8 @@ describe('Sort', function() {
}

done();
}, modelFactory);
});
});
});

it('should validate against the schema', function() {
var schema = schemaPath(transforms.sort.schema),
Expand All @@ -183,10 +183,10 @@ describe('Sort', function() {
expect(validate({ "type": "sort", "by": "price" })).to.be.true;
expect(validate({ "type": "sort", "by": "-price" })).to.be.true;
expect(validate({ "type": "sort", "by": ["price", "gdp"] })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "sort" })).to.be.false;
expect(validate({ "type": "sort", "by": true })).to.be.false;
});
});

});
64 changes: 32 additions & 32 deletions test/transforms/stack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Stack', function() {
{a: 3, b: 9, c: 'c'},
{a: 3, b: 2, c: 'a'}
];

function spec(opt) {
var stack = {
type: "stack",
Expand All @@ -24,7 +24,7 @@ describe('Stack', function() {
offset: opt.offset,
output: {start: "y2", end: "y", mid: "cy"}
};

return {
data: [{
name: "table",
Expand All @@ -36,7 +36,8 @@ describe('Stack', function() {

it('should perform flat stack', function(done) {
parseSpec(spec({groupby:null, sortby:null, field:"b", offset:"zero"}),
function(model) {
modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
y = [0,1,3,6,10,15,21,28,36,45,47];
Expand All @@ -47,13 +48,13 @@ describe('Stack', function() {
expect(data[i]).to.have.property('cy', 0.5 * (y[i] + y[i+1]));
}
done();
},
modelFactory);
});
});

it('should perform grouped stack', function(done) {
parseSpec(spec({groupby:["a"], sortby:null, field:"b", offset:"zero"}),
function(model) {
modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
y0 = [0,1,3, 0,4, 9, 0, 7,15,24],
Expand All @@ -65,13 +66,13 @@ describe('Stack', function() {
expect(data[i]).to.have.property('cy', 0.5 * (y0[i] + y1[i]));
}
done();
},
modelFactory);
});
});

it('should perform grouped stack with offset center', function(done) {
parseSpec(spec({groupby:["a"], sortby:null, field:"b", offset:"center"}),
function(model) {
modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
y0 = [10,11,13, 5.5, 9.5,14.5, 0, 7,15,24],
Expand All @@ -83,13 +84,13 @@ describe('Stack', function() {
expect(data[i]).to.have.property('cy', 0.5 * (y0[i] + y1[i]));
}
done();
},
modelFactory);
});
});

it('should perform grouped stack with offset normalize', function(done) {
parseSpec(spec({groupby:["a"], sortby:null, field:"b", offset:"normalize"}),
function(model) {
modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
y0 = [0/6,1/6,3/6, 0/15,4/15, 9/15, 0/26, 7/26,15/26,24/26],
Expand All @@ -101,13 +102,13 @@ describe('Stack', function() {
expect(data[i]).to.have.property('cy').closeTo(0.5 * (y0[i] + y1[i]), EPSILON);
}
done();
},
modelFactory);
});
});

it('should perform grouped sorted stack', function(done) {
parseSpec(spec({groupby:["a"], sortby:"c", field:"b", offset:"zero"}),
function(model) {
modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
y0 = [0,1,3, 0,4, 9, 0, 9,17,7],
Expand All @@ -119,44 +120,43 @@ describe('Stack', function() {
expect(data[i]).to.have.property('cy', 0.5 * (y0[i] + y1[i]));
}
done();
},
modelFactory);
});
});

it('should validate against the schema', function() {
var schema = schemaPath(transforms.stack.schema),
validate = validator(schema);

expect(validate({ "type": "stack", "groupby": ["country"], "field": "medals" })).to.be.true;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "sortby": ["gdp"] })).to.be.true;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "zero" })).to.be.true;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "center" })).to.be.true;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "normalize" })).to.be.true;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "output": {"start": "start", "mid": "mid", "end": "end"} })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "stack" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"] })).to.be.false;
expect(validate({ "type": "stack", "groupby": "country", "field": "medals" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"], "field": ["medals"] })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "sortby": "gdp" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "foo" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "output": {"foo": "bar"} })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "bar": "foo" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "silhouette" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "wiggle" })).to.be.false;
expect(validate({ "type": "stack", "groupby": ["country"],
expect(validate({ "type": "stack", "groupby": ["country"],
"field": "medals", "offset": "expand" })).to.be.false;
});
});
7 changes: 3 additions & 4 deletions test/transforms/treeify.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ describe('Treeify', function() {
}

it('should treeify a table', function(done) {
parseSpec(spec(),
function(model) {
parseSpec(spec(), modelFactory,
function(error, model) {
var ds = model.data('table'),
data = ds.values(),
root = data.filter(function(d) { return d.parent==null; });
Expand All @@ -31,8 +31,7 @@ describe('Treeify', function() {
expect(root[0].children[1].children).to.have.length(2);

done();
},
modelFactory);
});
});

it('should validate against the schema', function() {
Expand Down
4 changes: 2 additions & 2 deletions test/transforms/voronoi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ describe('Voronoi', function() {
};

it('should compute voronoi diagram', function(done) {
parseSpec(spec, function(model) {
parseSpec(spec, modelFactory, function(error, model) {
var data = model.data('table').values().sort(dl.comparator('p'));
expect(data[0].p).to.equal('M-100000,0L0,0L0,-100000L-100000,-100000Z');
expect(data[1].p).to.equal('M0,-100000L0,0L100000,0L100000,-100000Z');
expect(data[2].p).to.equal('M0,0L0,100000L100000,100000L100000,0Z');
expect(data[3].p).to.equal('M0,100000L0,0L-100000,0L-100000,100000Z');
done();
}, modelFactory);
});
});

it('should validate against the schema', function() {
Expand Down
10 changes: 5 additions & 5 deletions test/transforms/wordcloud.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Wordcloud', function() {
var s = dl.duplicate(spec);
s.data[0].transform[0].fontScale = null;

parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var data = model.data('table').values();
expect(data.length).to.equal(3);
expect(data[0].w).to.equal('a');
Expand All @@ -43,13 +43,13 @@ describe('Wordcloud', function() {
expect(data[2].layout_y).to.be.defined;

done();
}, modelFactory);
});
});

it('should layout scaled wordcloud', function(done) {
var s = dl.duplicate(spec);
s.data[0].transform[0].rotate = 'r';
parseSpec(s, function(model) {
parseSpec(s, modelFactory, function(error, model) {
var data = model.data('table').values();
expect(data.length).to.equal(3);
expect(data[0].w).to.equal('a');
Expand All @@ -69,7 +69,7 @@ describe('Wordcloud', function() {
expect(data[2].layout_y).to.be.defined;

done();
}, modelFactory);
});
});

it('should validate against the schema', function() {
Expand Down Expand Up @@ -110,7 +110,7 @@ describe('Wordcloud', function() {
expect(validate({ "type": "wordcloud", "output": {"rotate": "rotate"} })).to.be.true;

expect(validate({ "type": "foo" })).to.be.false;
expect(validate({ "type": "wordcloud", "size": 1 })).to.be.false;
expect(validate({ "type": "wordcloud", "size": 1 })).to.be.false;
expect(validate({ "type": "wordcloud", "font": 2 })).to.be.false;
expect(validate({ "type": "wordcloud", "fontStyle": 2 })).to.be.false;
expect(validate({ "type": "wordcloud", "fontWeight": 3 })).to.be.false;
Expand Down