@quartic/bokehjs
Version:
Interactive, novel data visualization
304 lines (239 loc) • 8.95 kB
text/coffeescript
{expect} = require "chai"
utils = require "../../utils"
sinon = require "sinon"
{HasProps} = utils.require "core/has_props"
p = utils.require "core/properties"
{CustomJS} = utils.require("models/callbacks/customjs")
{DataRange1d} = utils.require("models/ranges/data_range1d")
{GlyphRenderer} = utils.require("models/renderers/glyph_renderer")
class TestObject extends HasProps
type: 'TestObject'
@define {
renderers: [ p.Array, [] ]
}
describe "datarange1d module", ->
afterEach ->
utils.unstub_canvas()
utils.unstub_solver()
beforeEach ->
utils.stub_canvas()
utils.stub_solver()
describe "default creation", ->
r = new DataRange1d()
it "should have start = null", ->
expect(r.start).to.be.null
it "should have end = null", ->
expect(r.end).to.be.null
# Math.min(null, null) == 0
it "should have min = 0", ->
expect(r.min).to.be.equal 0
# Math.max(null, null) == 0
it "should have max = 0", ->
expect(r.max).to.be.equal 0
it "should have flipped = false", ->
expect(r.flipped).to.be.equal false
it "should have follow = null", ->
expect(r.follow).to.be.null
it "should have follow_interval = null", ->
expect(r.follow_interval).to.be.null
it "should have default_span = 2", ->
expect(r.default_span).to.be.equal 2
it "should have no computed_renderers", ->
expect(r.computed_renderers()).to.be.deep.equal []
describe "explicit bounds=(10,20) creation", ->
r = new DataRange1d({start: 10, end:20})
it "should have start = 10", ->
expect(r.start).to.be.equal 10
it "should have end = 20", ->
expect(r.end).to.be.equal 20
it "should have min = 10", ->
expect(r.min).to.be.equal 10
it "should have max = 20", ->
expect(r.max).to.be.equal 20
describe "reset", ->
it "should reset configuration to initial values", ->
r = new DataRange1d()
r.range_padding = 0.2
r.follow = 'end'
r.follow_interval = 10
r.default_span = 10
r.reset()
expect(r.range_padding).to.be.equal 0.1
expect(r.follow).to.be.null
expect(r.follow_interval).to.be.null
expect(r.default_span).to.be.equal 2
# something must call update(...) to update (start, end)
it "should not reset (start, end)", ->
r = new DataRange1d()
r.start = 4
r.end = 10
r.reset()
expect(r.start).to.be.equal 4
expect(r.end).to.be.equal 10
it "should execute callback exactly once", ->
cb = new CustomJS()
r = new DataRange1d({callback: cb})
spy = sinon.spy(cb, 'execute')
r.reset()
expect(spy.calledOnce).to.be.true
describe "computed_renderers", ->
it "should add renderers from one plot", ->
r = new DataRange1d()
p = new TestObject()
g = new GlyphRenderer()
p.renderers = [g]
r.plots = [p]
expect(r.computed_renderers()).to.be.deep.equal [g]
r = new DataRange1d()
g2 = new GlyphRenderer()
p.renderers = [g, g2]
r.plots = [p]
expect(r.computed_renderers()).to.be.deep.equal [g, g2]
it "should add renderers from multiple plot", ->
r = new DataRange1d()
p = new TestObject()
g = new GlyphRenderer()
p.renderers = [g]
p2 = new TestObject()
g2 = new GlyphRenderer()
p2.renderers = [g2]
r.plots = [p, p2]
expect(r.computed_renderers()).to.be.deep.equal [g, g2]
it "should respect user-set renderers", ->
r = new DataRange1d()
p = new TestObject()
g = new GlyphRenderer()
p.renderers = [g]
p2 = new TestObject()
g2 = new GlyphRenderer()
p2.renderers = [g2]
r.plots = [p, p2]
r.renderers = [g2]
expect(r.computed_renderers()).to.be.deep.equal [g2]
describe "_compute_range", ->
it "should use default_span when max=min", ->
r = new DataRange1d()
expect(r._compute_range(3, 3)).to.be.deep.equal [2, 4]
r.default_span = 4
expect(r._compute_range(3, 3)).to.be.deep.equal [1, 5]
it "should use default_span as powers of 10 when mapper_hint='log'", ->
r = new DataRange1d()
r.mapper_hint = "log"
expect(r._compute_range(100, 100)).to.be.deep.equal [9.988493699365053, 1001.1519555381683]
r.default_span = 4
expect(r._compute_range(100, 100)).to.be.deep.equal [0.9988493699365047, 10011.519555381703]
it "should swap max, min when flipped", ->
r = new DataRange1d()
r.flipped = true
expect(r._compute_range(3, 3)).to.be.deep.equal [4, 2]
it "should follow min when follow=start and not flipped", ->
r = new DataRange1d()
r.range_padding = 0
r.follow = 'start'
r.follow_interval = 4
expect(r._compute_range(1, 3)).to.be.deep.equal [1, 3]
expect(r._compute_range(1, 7)).to.be.deep.equal [1, 5]
it "should follow max when follow=start and flipped", ->
r = new DataRange1d()
r.range_padding = 0
r.follow = 'start'
r.follow_interval = 4
r.flipped = true
expect(r._compute_range(1, 3)).to.be.deep.equal [3, 1]
expect(r._compute_range(1, 7)).to.be.deep.equal [7, 3]
it "should follow max when follow=end and not flipped", ->
r = new DataRange1d()
r.range_padding = 0
r.follow = 'end'
r.follow_interval = 4
expect(r._compute_range(1, 3)).to.be.deep.equal [1, 3]
expect(r._compute_range(1, 7)).to.be.deep.equal [3, 7]
it "should follow min when follow=end and flipped", ->
r = new DataRange1d()
r.range_padding = 0
r.follow = 'end'
r.follow_interval = 4
r.flipped = true
expect(r._compute_range(1, 3)).to.be.deep.equal [3, 1]
expect(r._compute_range(1, 7)).to.be.deep.equal [5, 1]
it "should apply range_padding", ->
r = new DataRange1d()
r.range_padding = 0.5
expect(r._compute_range(1, 3)).to.be.deep.equal [0.5, 3.5]
it "should apply range_padding logly when mapper_hint='log'", ->
r = new DataRange1d()
r.range_padding = 0.5
r.mapper_hint = "log"
expect(r._compute_range(0.01, 10)).to.be.deep.equal [0.0017782794100389264, 56.23413251903488]
describe "_compute_min_max", ->
it "should compute max/min for dimension of a single plot_bounds", ->
r = new DataRange1d()
bds = {
1: {minX: 0, maxX: 10, minY: 5, maxY:6}
}
expect(r._compute_min_max(bds, 0)).to.be.deep.equal [0, 10]
expect(r._compute_min_max(bds, 1)).to.be.deep.equal [5, 6]
it "should compute max/min for dimension of multiple plot_bounds", ->
r = new DataRange1d()
bds = {
1: {minX: 0, maxX: 10, minY: 5, maxY: 6}
2: {minX: 0, maxX: 15, minY: 5.5, maxY: 5.6}
}
expect(r._compute_min_max(bds, 0)).to.be.deep.equal [0, 15]
expect(r._compute_min_max(bds, 1)).to.be.deep.equal [5, 6]
bds = {
1: {minX: 0, maxX: 10, minY: 5, maxY: 6}
2: {minX: 0, maxX: 15, minY: 5.5, maxY: 5.6}
3: {minX: -10, maxX: 15, minY: 0, maxY: 2}
}
expect(r._compute_min_max(bds, 0)).to.be.deep.equal [-10, 15]
expect(r._compute_min_max(bds, 1)).to.be.deep.equal [0, 6]
describe "_computed_plot_bounds", ->
it "should compute bounds from configured renderers", ->
r = new DataRange1d()
g1 = new TestObject()
g1.id = 1
g2 = new TestObject()
g2.id = 2
bds = {
1: {minX: 0, maxX: 10, minY: 5, maxY: 6}
2: {minX: 0, maxX: 15, minY: 5.5, maxY: 5.6}
3: {minX: -10, maxX: 15, minY: 0, maxY: 2}
}
expect(r._compute_plot_bounds([g1], bds)).to.be.deep.equal {minX: 0, maxX: 10, minY: 5, maxY: 6}
expect(r._compute_plot_bounds([g1, g2], bds)).to.be.deep.equal {minX: 0, maxX: 15, minY: 5, maxY: 6}
describe "update", ->
it "should update its start and end values", ->
r = new DataRange1d()
p = new TestObject()
g = new GlyphRenderer()
g.id = "id"
p.renderers = [g]
r.plots = [p]
bds = {
"id": {minX: -10, maxX: -6, minY: 5, maxY: 6}
}
r.update(bds, 0, "id")
expect(r.start).to.be.equal -10.2
it "should not update its start or end values to NaN when log", ->
r = new DataRange1d()
r.mapper_hint = "log"
p = new TestObject()
g = new GlyphRenderer()
g.id = "id"
p.renderers = [g]
r.plots = [p]
bds = {
"id": {minX: Infinity, maxX: -Infinity, minY: 5, maxY: 6}
}
r.update(bds, 0, "id")
expect(r.start).not.to.be.NaN
expect(r.end).not.to.be.NaN
describe "changing model attribute", ->
it "should execute callback once", ->
cb = new CustomJS()
spy = sinon.spy(cb, 'execute')
r = new DataRange1d({callback: cb})
expect(spy.called).to.be.false
r.start = 15
expect(spy.calledOnce).to.be.true