Initial commit

This commit is contained in:
Khanh Ngo
2015-12-13 16:34:12 +07:00
commit 2dac8205f6
3113 changed files with 514935 additions and 0 deletions

View File

@ -0,0 +1,60 @@
describe 'Morris.Area', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
lineColors: [ '#0b62a4', '#7a92a3']
gridLineColor: '#aaa'
xkey: 'x'
ykeys: ['y']
labels: ['Y']
it 'should contain a line path for each line', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain a path with stroke-width 0 for each line', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain 5 grid lines', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
it 'should contain 9 text elements', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("text").size().should.equal 9
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['Y']
lineColors: [ '#0b62a4', '#7a92a3']
lineWidth: 3
pointWidths: [5]
pointStrokeColors: ['#ffffff']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
it 'should not be cumulative if behaveLikeLine', ->
chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
chart.cumulative.should.equal false
it 'should have a line with transparent fill if behaveLikeLine', ->
chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
$('#graph').find("path[fill-opacity='0.8']").size().should.equal 1
it 'should not have a line with transparent fill', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[fill-opacity='0.8']").size().should.equal 0
it 'should have a line with the fill of a modified line color', ->
chart = Morris.Area $.extend {}, defaults
$('#graph').find("path[fill='#0b62a4']").size().should.equal 0
$('#graph').find("path[fill='#7a92a3']").size().should.equal 0

View File

@ -0,0 +1,127 @@
describe 'Morris.Bar', ->
describe 'when using vertical grid', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
barColors: [ '#0b62a4', '#7a92a3']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
verticalGridCondition: (index) -> index % 2
verticalGridColor: '#888888'
verticalGridOpacity: '0.2'
describe 'svg structure', ->
it 'should contain extra rectangles for vertical grid', ->
$('#graph').css('height', '250px').css('width', '800px')
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect").size().should.equal 6
describe 'svg attributes', ->
it 'should have to bars with verticalGrid.color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[fill='#{defaults.verticalGridColor}']").size().should.equal 2
it 'should have to bars with verticalGrid.color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[fill-opacity='#{defaults.verticalGridOpacity}']").size().should.equal 2
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
it 'should contain a rect for each bar', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect").size().should.equal 4
it 'should contain 5 grid lines', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("path").size().should.equal 5
it 'should contain 7 text elements', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text").size().should.equal 7
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
barColors: [ '#0b62a4', '#7a92a3']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
it 'should have a bar with the first default color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[fill='#0b62a4']").size().should.equal 2
it 'should have a bar with no stroke', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[stroke='none']").size().should.equal 4
it 'should have text with configured fill color', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text[fill='#888888']").size().should.equal 7
it 'should have text with configured font size', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("text[font-size='12px']").size().should.equal 7
describe 'when setting bar radius', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
barRadius: [5, 5, 0, 0]
it 'should contain a path for each bar', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("path").size().should.equal 9
it 'should use rects if radius is too big', ->
delete defaults.barStyle
chart = Morris.Bar $.extend {}, defaults,
barRadius: [300, 300, 0, 0]
$('#graph').find("rect").size().should.equal 4
describe 'barSize option', ->
describe 'svg attributes', ->
defaults =
element: 'graph'
barSize: 20
data: [
{x: '2011 Q1', y: 3, z: 2, a: 3}
{x: '2011 Q2', y: 2, z: null, a: 1}
{x: '2011 Q3', y: 0, z: 2, a: 4}
{x: '2011 Q4', y: 2, z: 4, a: 3}
],
xkey: 'x'
ykeys: ['y', 'z', 'a']
labels: ['Y', 'Z', 'A']
it 'should calc the width if too narrow for barSize', ->
$('#graph').width('200px')
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect").filter((i) ->
parseFloat($(@).attr('width'), 10) < 10
).size().should.equal 11
it 'should set width to @options.barSize if possible', ->
chart = Morris.Bar $.extend {}, defaults
$('#graph').find("rect[width='#{defaults.barSize}']").size().should.equal 11

View File

@ -0,0 +1,36 @@
describe 'Morris.Bar#colorFor', ->
defaults =
element: 'graph'
data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
it 'should fetch colours from an array', ->
chart = Morris.Bar $.extend {}, defaults, barColors: ['#f00', '#0f0', '#00f']
chart.colorFor(chart.data[0], 0, 'bar').should.equal '#f00'
chart.colorFor(chart.data[0], 0, 'hover').should.equal '#f00'
chart.colorFor(chart.data[0], 1, 'bar').should.equal '#0f0'
chart.colorFor(chart.data[0], 1, 'hover').should.equal '#0f0'
chart.colorFor(chart.data[0], 2, 'bar').should.equal '#00f'
chart.colorFor(chart.data[0], 2, 'hover').should.equal '#00f'
chart.colorFor(chart.data[0], 3, 'bar').should.equal '#f00'
chart.colorFor(chart.data[0], 4, 'hover').should.equal '#0f0'
it 'should defer to a callback', ->
stub = sinon.stub().returns '#f00'
chart = Morris.Bar $.extend {}, defaults, barColors: stub
stub.reset()
chart.colorFor(chart.data[0], 0, 'bar')
stub.should.have.been.calledWith(
{x:0, y:2, label:'foo'},
{index:0, key:'y', label:'Y'},
'bar')
chart.colorFor(chart.data[0], 1, 'hover')
stub.should.have.been.calledWith(
{x:0, y:3, label:'foo'},
{index:1, key:'z', label:'Z'},
'hover')

View File

@ -0,0 +1,38 @@
describe '#commas', ->
it 'should insert commas into long numbers', ->
# zero
Morris.commas(0).should.equal("0")
# positive integers
Morris.commas(1).should.equal("1")
Morris.commas(12).should.equal("12")
Morris.commas(123).should.equal("123")
Morris.commas(1234).should.equal("1,234")
Morris.commas(12345).should.equal("12,345")
Morris.commas(123456).should.equal("123,456")
Morris.commas(1234567).should.equal("1,234,567")
# negative integers
Morris.commas(-1).should.equal("-1")
Morris.commas(-12).should.equal("-12")
Morris.commas(-123).should.equal("-123")
Morris.commas(-1234).should.equal("-1,234")
Morris.commas(-12345).should.equal("-12,345")
Morris.commas(-123456).should.equal("-123,456")
Morris.commas(-1234567).should.equal("-1,234,567")
# positive decimals
Morris.commas(1.2).should.equal("1.2")
Morris.commas(12.34).should.equal("12.34")
Morris.commas(123.456).should.equal("123.456")
Morris.commas(1234.56).should.equal("1,234.56")
# negative decimals
Morris.commas(-1.2).should.equal("-1.2")
Morris.commas(-12.34).should.equal("-12.34")
Morris.commas(-123.456).should.equal("-123.456")
Morris.commas(-1234.56).should.equal("-1,234.56")
# null
Morris.commas(null).should.equal('-')

View File

@ -0,0 +1,76 @@
describe 'Morris.Donut', ->
describe 'svg structure', ->
defaults =
element: 'graph'
data: [ {label: 'Jam', value: 25 },
{label: 'Frosted', value: 40 },
{label: 'Custard', value: 25 },
{label: 'Sugar', value: 10 } ]
formatter: (y) -> "#{y}%"
it 'should contain 2 paths for each segment', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path").size().should.equal 8
it 'should contain 2 text elements for the label', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text").size().should.equal 2
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [ {label: 'Jam', value: 25 },
{label: 'Frosted', value: 40 },
{label: 'Custard', value: 25 },
{label: 'Sugar', value: 10 } ]
formatter: (y) -> "#{y}%"
colors: [ '#0B62A4', '#3980B5', '#679DC6', '#95BBD7']
it 'should have a label with font size 15', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-size='15px']").size().should.equal 1
it 'should have a label with font size 14', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-size='14px']").size().should.equal 1
it 'should have a label with font-weight 800', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("text[font-weight='800']").size().should.equal 1
it 'should have 1 paths with fill of first color', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[fill='#0b62a4']").size().should.equal 1
it 'should have 1 paths with stroke of first color', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should have a path with white stroke', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke='#ffffff']").size().should.equal 4
it 'should have a path with stroke-width 3', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke-width='3']").size().should.equal 4
it 'should have a path with stroke-width 2', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke-width='2']").size().should.equal 4
describe 'setData', ->
defaults =
element: 'graph'
data: [ {label: 'One', value: 25 }, {label: "Two", value: 30} ]
colors: ['#ff0000', '#00ff00', '#0000ff']
it 'should update the chart', ->
chart = Morris.Donut $.extend {}, defaults
$('#graph').find("path[stroke='#0000ff']").size().should.equal 0
chart.setData [
{ label: 'One', value: 25 }
{ label: 'Two', value: 30 }
{ label: 'Three', value: 35 }
]
$('#graph').find("path[stroke='#0000ff']").size().should.equal 1

View File

@ -0,0 +1,25 @@
describe 'Morris.Grid#autoGridLines', ->
beforeEach ->
@subject = Morris.Grid.prototype.autoGridLines
it 'should draw at fixed intervals', ->
@subject(0, 4, 5).should.deep.equal [0, 1, 2, 3, 4]
@subject(0, 400, 5).should.deep.equal [0, 100, 200, 300, 400]
it 'should pick intervals that show significant numbers', ->
@subject(102, 499, 5).should.deep.equal [100, 200, 300, 400, 500]
it 'should draw zero when it falls within [ymin..ymax]', ->
@subject(-100, 300, 5).should.deep.equal [-100, 0, 100, 200, 300]
@subject(-50, 350, 5).should.deep.equal [-125, 0, 125, 250, 375]
@subject(-400, 400, 5).should.deep.equal [-400, -200, 0, 200, 400]
@subject(100, 500, 5).should.deep.equal [100, 200, 300, 400, 500]
@subject(-500, -100, 5).should.deep.equal [-500, -400, -300, -200, -100]
it 'should generate decimal labels to 2 significant figures', ->
@subject(0, 1, 5).should.deep.equal [0, 0.25, 0.5, 0.75, 1]
@subject(0.1, 0.5, 5).should.deep.equal [0.1, 0.2, 0.3, 0.4, 0.5]
it 'should use integer intervals for intervals larger than 1', ->
@subject(0, 9, 5).should.deep.equal [0, 3, 6, 9, 12]

View File

@ -0,0 +1,208 @@
describe 'Morris.Grid#setData', ->
it 'should not alter user-supplied data', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
expected_data = [{x: 1, y: 1}, {x: 2, y: 2}]
Morris.Line
element: 'graph'
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
my_data.should.deep.equal expected_data
describe 'ymin/ymax', ->
beforeEach ->
@defaults =
element: 'graph'
xkey: 'x'
ykeys: ['y', 'z']
labels: ['y', 'z']
it 'should use a user-specified minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 1}]
ymin: 10
ymax: 20
line.ymin.should.equal 10
line.ymax.should.equal 20
describe 'auto', ->
it 'should automatically calculate the minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto'
ymax: 'auto'
line.ymin.should.equal 10
line.ymax.should.equal 15
it 'should automatically calculate the minimum and maximum value given no y data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto'
ymax: 'auto'
line.ymin.should.equal 0
line.ymax.should.equal 1
describe 'auto [n]', ->
it 'should automatically calculate the minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto 11'
ymax: 'auto 13'
line.ymin.should.equal 10
line.ymax.should.equal 15
it 'should automatically calculate the minimum and maximum value given no data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto 11'
ymax: 'auto 13'
line.ymin.should.equal 11
line.ymax.should.equal 13
it 'should use a user-specified minimum and maximum value', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
ymin: 'auto 5'
ymax: 'auto 20'
line.ymin.should.equal 5
line.ymax.should.equal 20
it 'should use a user-specified minimum and maximum value given no data', ->
line = Morris.Line $.extend @defaults,
data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
ymin: 'auto 5'
ymax: 'auto 20'
line.ymin.should.equal 5
line.ymax.should.equal 20
describe 'xmin/xmax', ->
it 'should calculate the horizontal range', ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: 2}, {x: 1, y: 1}, {x: 4, y: 4}, {x: 3, y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.xmin.should == 1
line.xmax.should == 4
it "should pad the range if there's only one data point", ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: 2}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.xmin.should == 1
line.xmax.should == 3
describe 'sorting', ->
it 'should sort data when parseTime is true', ->
line = Morris.Line
element: 'graph'
data: [
{x: '2012 Q1', y: 2},
{x: '2012 Q3', y: 1},
{x: '2012 Q4', y: 4},
{x: '2012 Q2', y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.map((row) -> row.label).should.deep.equal ['2012 Q1', '2012 Q2', '2012 Q3', '2012 Q4']
it 'should not sort data when parseTime is false', ->
line = Morris.Line
element: 'graph'
data: [{x: 1, y: 2}, {x: 4, y: 1}, {x: 3, y: 4}, {x: 2, y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
parseTime: false
line.data.map((row) -> row.label).should.deep.equal [1, 4, 3, 2]
describe 'timestamp data', ->
it 'should generate default labels for timestamp x-values', ->
d = [
new Date 2012, 0, 1
new Date 2012, 0, 2
new Date 2012, 0, 3
new Date 2012, 0, 4
]
line = Morris.Line
element: 'graph'
data: [
{x: d[0].getTime(), y: 2},
{x: d[1].getTime(), y: 1},
{x: d[2].getTime(), y: 4},
{x: d[3].getTime(), y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.map((row) -> row.label).should.deep.equal d.map((t) -> t.toString())
it 'should use a user-supplied formatter for labels', ->
line = Morris.Line
element: 'graph'
data: [
{x: new Date(2012, 0, 1).getTime(), y: 2},
{x: new Date(2012, 0, 2).getTime(), y: 1},
{x: new Date(2012, 0, 3).getTime(), y: 4},
{x: new Date(2012, 0, 4).getTime(), y: 3}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
dateFormat: (ts) ->
date = new Date(ts)
"#{date.getFullYear()}-#{date.getMonth()+1}-#{date.getDate()}"
line.data.map((row) -> row.label).should.deep.equal ['2012-1-1', '2012-1-2', '2012-1-3', '2012-1-4']
it 'should parse y-values in strings', ->
line = Morris.Line
element: 'graph'
data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.ymin.should == 12
line.ymax.should == 16
line.data.map((row) -> row.y).should.deep.equal [[13.5], [12], [16], [14]]
it 'should clear the chart when empty data is supplied', ->
line = Morris.Line
element: 'graph',
data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.length.should.equal 4
line.setData([])
line.data.length.should.equal 0
line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
line.data.length.should.equal 4
it 'should be able to add data if the chart is initialised with empty data', ->
line = Morris.Line
element: 'graph',
data: []
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.data.length.should.equal 0
line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
line.data.length.should.equal 4
it 'should automatically choose significant numbers for y-labels', ->
line = Morris.Line
element: 'graph',
data: [{x: 1, y: 0}, {x: 2, y: 3600}]
xkey: 'x'
ykeys: ['y']
labels: ['y']
line.grid.should == [0, 1000, 2000, 3000, 4000]

View File

@ -0,0 +1,15 @@
describe 'Morris.Grid#yLabelFormat', ->
it 'should use custom formatter for y labels', ->
formatter = (label) ->
flabel = parseFloat(label) / 1000
"#{flabel.toFixed(1)}k"
line = Morris.Line
element: 'graph'
data: [{x: 1, y: 1500}, {x: 2, y: 2500}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
preUnits: "$"
yLabelFormat: formatter
line.yLabelFormat(1500).should.equal "1.5k"

View File

@ -0,0 +1,64 @@
describe "Morris.Hover", ->
describe "with dummy content", ->
beforeEach ->
parent = $('<div style="width:200px;height:180px"></div>')
.appendTo($('#test'))
@hover = new Morris.Hover(parent: parent)
@element = $('#test .morris-hover')
it "should initialise a hidden, empty popup", ->
@element.should.exist
@element.should.be.hidden
@element.should.be.empty
describe "#show", ->
it "should show the popup", ->
@hover.show()
@element.should.be.visible
describe "#hide", ->
it "should hide the popup", ->
@hover.show()
@hover.hide()
@element.should.be.hidden
describe "#html", ->
it "should replace the contents of the element", ->
@hover.html('<div>Foobarbaz</div>')
@element.should.have.html('<div>Foobarbaz</div>')
describe "#moveTo", ->
beforeEach ->
@hover.html('<div style="width:84px;height:84px"></div>')
it "should place the popup directly above the given point", ->
@hover.moveTo(100, 150)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
it "should place the popup below the given point if it does not fit above", ->
@hover.moveTo(100, 50)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '60px')
it "should center the popup vertically if it will not fit above or below", ->
@hover.moveTo(100, 100)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
it "should center the popup vertically if no y value is supplied", ->
@hover.moveTo(100)
@element.should.have.css('left', '50px')
@element.should.have.css('top', '40px')
describe "#update", ->
it "should update content, show and reposition the popup", ->
hover = new Morris.Hover(parent: $('#test'))
html = "<div style='width:84px;height:84px'>Hello, Everyone!</div>"
hover.update(html, 150, 200)
el = $('#test .morris-hover')
el.should.have.css('left', '100px')
el.should.have.css('top', '90px')
el.should.have.text('Hello, Everyone!')

View File

@ -0,0 +1,186 @@
describe '#labelSeries', ->
it 'should generate decade intervals', ->
Morris.labelSeries(
new Date(1952, 0, 1).getTime(),
new Date(2012, 0, 1).getTime(),
1000
).should.deep.equal([
["1960", new Date(1960, 0, 1).getTime()],
["1970", new Date(1970, 0, 1).getTime()],
["1980", new Date(1980, 0, 1).getTime()],
["1990", new Date(1990, 0, 1).getTime()],
["2000", new Date(2000, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()]
])
Morris.labelSeries(
new Date(1952, 3, 1).getTime(),
new Date(2012, 3, 1).getTime(),
1000
).should.deep.equal([
["1960", new Date(1960, 0, 1).getTime()],
["1970", new Date(1970, 0, 1).getTime()],
["1980", new Date(1980, 0, 1).getTime()],
["1990", new Date(1990, 0, 1).getTime()],
["2000", new Date(2000, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()]
])
it 'should generate year intervals', ->
Morris.labelSeries(
new Date(2007, 0, 1).getTime(),
new Date(2012, 0, 1).getTime(),
1000
).should.deep.equal([
["2007", new Date(2007, 0, 1).getTime()],
["2008", new Date(2008, 0, 1).getTime()],
["2009", new Date(2009, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()],
["2011", new Date(2011, 0, 1).getTime()],
["2012", new Date(2012, 0, 1).getTime()]
])
Morris.labelSeries(
new Date(2007, 3, 1).getTime(),
new Date(2012, 3, 1).getTime(),
1000
).should.deep.equal([
["2008", new Date(2008, 0, 1).getTime()],
["2009", new Date(2009, 0, 1).getTime()],
["2010", new Date(2010, 0, 1).getTime()],
["2011", new Date(2011, 0, 1).getTime()],
["2012", new Date(2012, 0, 1).getTime()]
])
it 'should generate month intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 5, 1).getTime(),
1000
).should.deep.equal([
["2012-01", new Date(2012, 0, 1).getTime()],
["2012-02", new Date(2012, 1, 1).getTime()],
["2012-03", new Date(2012, 2, 1).getTime()],
["2012-04", new Date(2012, 3, 1).getTime()],
["2012-05", new Date(2012, 4, 1).getTime()],
["2012-06", new Date(2012, 5, 1).getTime()]
])
it 'should generate week intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 1, 10).getTime(),
1000
).should.deep.equal([
["2012-01-01", new Date(2012, 0, 1).getTime()],
["2012-01-08", new Date(2012, 0, 8).getTime()],
["2012-01-15", new Date(2012, 0, 15).getTime()],
["2012-01-22", new Date(2012, 0, 22).getTime()],
["2012-01-29", new Date(2012, 0, 29).getTime()],
["2012-02-05", new Date(2012, 1, 5).getTime()]
])
it 'should generate day intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 0, 6).getTime(),
1000
).should.deep.equal([
["2012-01-01", new Date(2012, 0, 1).getTime()],
["2012-01-02", new Date(2012, 0, 2).getTime()],
["2012-01-03", new Date(2012, 0, 3).getTime()],
["2012-01-04", new Date(2012, 0, 4).getTime()],
["2012-01-05", new Date(2012, 0, 5).getTime()],
["2012-01-06", new Date(2012, 0, 6).getTime()]
])
it 'should generate hour intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0).getTime(),
new Date(2012, 0, 1, 5).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0).getTime()],
["01:00", new Date(2012, 0, 1, 1).getTime()],
["02:00", new Date(2012, 0, 1, 2).getTime()],
["03:00", new Date(2012, 0, 1, 3).getTime()],
["04:00", new Date(2012, 0, 1, 4).getTime()],
["05:00", new Date(2012, 0, 1, 5).getTime()]
])
it 'should generate half-hour intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0, 0).getTime(),
new Date(2012, 0, 1, 2, 30).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
["01:30", new Date(2012, 0, 1, 1, 30).getTime()],
["02:00", new Date(2012, 0, 1, 2, 0).getTime()],
["02:30", new Date(2012, 0, 1, 2, 30).getTime()]
])
Morris.labelSeries(
new Date(2012, 4, 12, 0, 0).getTime(),
new Date(2012, 4, 12, 2, 30).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
["01:30", new Date(2012, 4, 12, 1, 30).getTime()],
["02:00", new Date(2012, 4, 12, 2, 0).getTime()],
["02:30", new Date(2012, 4, 12, 2, 30).getTime()]
])
it 'should generate fifteen-minute intervals', ->
Morris.labelSeries(
new Date(2012, 0, 1, 0, 0).getTime(),
new Date(2012, 0, 1, 1, 15).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
["00:15", new Date(2012, 0, 1, 0, 15).getTime()],
["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
["00:45", new Date(2012, 0, 1, 0, 45).getTime()],
["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
["01:15", new Date(2012, 0, 1, 1, 15).getTime()]
])
Morris.labelSeries(
new Date(2012, 4, 12, 0, 0).getTime(),
new Date(2012, 4, 12, 1, 15).getTime(),
1000
).should.deep.equal([
["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
["00:15", new Date(2012, 4, 12, 0, 15).getTime()],
["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
["00:45", new Date(2012, 4, 12, 0, 45).getTime()],
["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
["01:15", new Date(2012, 4, 12, 1, 15).getTime()]
])
it 'should override automatic intervals', ->
Morris.labelSeries(
new Date(2011, 11, 12).getTime(),
new Date(2012, 0, 12).getTime(),
1000,
"year"
).should.deep.equal([
["2012", new Date(2012, 0, 1).getTime()]
])
it 'should apply custom formatters', ->
Morris.labelSeries(
new Date(2012, 0, 1).getTime(),
new Date(2012, 0, 6).getTime(),
1000,
"day",
(d) -> "#{d.getMonth()+1}/#{d.getDate()}/#{d.getFullYear()}"
).should.deep.equal([
["1/1/2012", new Date(2012, 0, 1).getTime()],
["1/2/2012", new Date(2012, 0, 2).getTime()],
["1/3/2012", new Date(2012, 0, 3).getTime()],
["1/4/2012", new Date(2012, 0, 4).getTime()],
["1/5/2012", new Date(2012, 0, 5).getTime()],
["1/6/2012", new Date(2012, 0, 6).getTime()]
])

View File

@ -0,0 +1,211 @@
describe 'Morris.Line', ->
it 'should raise an error when the placeholder element is not found', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
fn = ->
Morris.Line(
element: "thisplacedoesnotexist"
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
)
fn.should.throw(/Graph container element not found/)
it 'should make point styles customizable', ->
my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
red = '#ff0000'
blue = '#0000ff'
chart = Morris.Line
element: 'graph'
data: my_data
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
pointStrokeColors: [red, blue]
pointStrokeWidths: [1, 2]
pointFillColors: [null, red]
chart.pointStrokeWidthForSeries(0).should.equal 1
chart.pointStrokeColorForSeries(0).should.equal red
chart.pointStrokeWidthForSeries(1).should.equal 2
chart.pointStrokeColorForSeries(1).should.equal blue
chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line')
chart.colorFor(chart.data[1], 1, 'point').should.equal red
describe 'generating column labels', ->
it 'should use user-supplied x value strings by default', ->
chart = Morris.Line
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
chart.data.map((x) -> x.label).should == ['2012 Q1', '2012 Q2']
it 'should use a default format for timestamp x-values', ->
d1 = new Date(2012, 0, 1)
d2 = new Date(2012, 0, 2)
chart = Morris.Line
element: 'graph'
data: [{x: d1.getTime(), y: 1}, {x: d2.getTime(), y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
chart.data.map((x) -> x.label).should == [d2.toString(), d1.toString()]
it 'should use user-defined formatters', ->
d = new Date(2012, 0, 1)
chart = Morris.Line
element: 'graph'
data: [{x: d.getTime(), y: 1}, {x: '2012-01-02', y: 1}]
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
dateFormat: (d) ->
x = new Date(d)
"#{x.getYear()}/#{x.getMonth()+1}/#{x.getDay()}"
chart.data.map((x) -> x.label).should == ['2012/1/1', '2012/1/2']
describe 'rendering lines', ->
beforeEach ->
@defaults =
element: 'graph'
data: [{x:0, y:1, z:0}, {x:1, y:0, z:1}, {x:2, y:1, z:0}, {x:3, y:0, z:1}, {x:4, y:1, z:0}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['y', 'z']
lineColors: ['#abcdef', '#fedcba']
smooth: true
shouldHavePath = (regex, color = '#abcdef') ->
# Matches an SVG path element within the rendered chart.
#
# Sneakily uses line colors to differentiate between paths within
# the chart.
$('#graph').find("path[stroke='#{color}']").attr('d').should.match regex
it 'should generate smooth lines when options.smooth is true', ->
Morris.Line @defaults
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/
it 'should generate jagged lines when options.smooth is false', ->
Morris.Line $.extend(@defaults, smooth: false)
shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/
it 'should generate smooth/jagged lines according to the value for each series when options.smooth is an array', ->
Morris.Line $.extend(@defaults, smooth: ['y'])
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/, '#abcdef'
shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/, '#fedcba'
it 'should ignore undefined values', ->
@defaults.data[2].y = undefined
Morris.Line @defaults
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){3}/
it 'should break the line at null values', ->
@defaults.data[2].y = null
Morris.Line @defaults
shouldHavePath /(M[\d\.]+,[\d\.]+C[\d\.]+(,[\d\.]+){5}){2}/
it 'should make line width customizable', ->
chart = Morris.Line $.extend(@defaults, lineWidth: [1, 2])
chart.lineWidthForSeries(0).should.equal 1
chart.lineWidthForSeries(1).should.equal 2
describe '#createPath', ->
it 'should generate a smooth line', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M0,10C2.5,7.5,7.5,0,10,0C12.5,0,17.5,7.5,20,10'
it 'should generate a jagged line', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, false, 20)
path.should.equal 'M0,10L10,0L20,10'
it 'should prevent paths from descending below the bottom of the chart', ->
testData = [{x: 0, y: 20}, {x: 10, y: 30}, {x: 20, y: 10}]
path = Morris.Line.createPath(testData, true, 30)
path.should.equal 'M0,20C2.5,22.5,7.5,30,10,30C12.5,28.75,17.5,15,20,10'
it 'should break the line at null values', ->
testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: null}, {x: 30, y: 10}, {x: 40, y: 0}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M0,10C2.5,7.5,7.5,2.5,10,0M30,10C32.5,7.5,37.5,2.5,40,0'
it 'should ignore leading and trailing null values', ->
testData = [{x: 0, y: null}, {x: 10, y: 10}, {x: 20, y: 0}, {x: 30, y: 10}, {x: 40, y: null}]
path = Morris.Line.createPath(testData, true, 20)
path.should.equal 'M10,10C12.5,7.5,17.5,0,20,0C22.5,0,27.5,7.5,30,10'
describe 'svg structure', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
lineColors: [ '#0b62a4', '#7a92a3']
xkey: 'x'
ykeys: ['y']
labels: ['dontcare']
it 'should contain a path that represents the line', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
it 'should contain a circle for each data point', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle").size().should.equal 2
it 'should contain 5 grid lines', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
it 'should contain 9 text elements', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text").size().should.equal 9
describe 'svg attributes', ->
defaults =
element: 'graph'
data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
xkey: 'x'
ykeys: ['y', 'z']
labels: ['Y', 'Z']
lineColors: [ '#0b62a4', '#7a92a3']
lineWidth: 3
pointStrokeWidths: [5]
pointStrokeColors: ['#ffffff']
gridLineColor: '#aaa'
gridStrokeWidth: 0.5
gridTextColor: '#888'
gridTextSize: 12
pointSize: [5]
it 'should have circles with configured fill color', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[fill='#0b62a4']").size().should.equal 2
it 'should have circles with configured stroke width', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[stroke-width='5']").size().should.equal 2
it 'should have circles with configured stroke color', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[stroke='#ffffff']").size().should.equal 2
it 'should have line with configured line width', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("path[stroke-width='3']").size().should.equal 1
it 'should have text with configured font size', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text[font-size='12px']").size().should.equal 9
it 'should have text with configured font size', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("text[fill='#888888']").size().should.equal 9
it 'should have circle with configured size', ->
chart = Morris.Line $.extend {}, defaults
$('#graph').find("circle[r='5']").size().should.equal 2

View File

@ -0,0 +1,17 @@
describe '#pad', ->
it 'should pad numbers', ->
Morris.pad2(0).should.equal("00")
Morris.pad2(1).should.equal("01")
Morris.pad2(2).should.equal("02")
Morris.pad2(3).should.equal("03")
Morris.pad2(4).should.equal("04")
Morris.pad2(5).should.equal("05")
Morris.pad2(6).should.equal("06")
Morris.pad2(7).should.equal("07")
Morris.pad2(8).should.equal("08")
Morris.pad2(9).should.equal("09")
Morris.pad2(10).should.equal("10")
Morris.pad2(12).should.equal("12")
Morris.pad2(34).should.equal("34")
Morris.pad2(123).should.equal("123")

View File

@ -0,0 +1,35 @@
describe '#parseTime', ->
it 'should parse years', ->
Morris.parseDate('2012').should.equal(new Date(2012, 0, 1).getTime())
it 'should parse quarters', ->
Morris.parseDate('2012 Q1').should.equal(new Date(2012, 2, 1).getTime())
it 'should parse months', ->
Morris.parseDate('2012-09').should.equal(new Date(2012, 8, 1).getTime())
Morris.parseDate('2012-10').should.equal(new Date(2012, 9, 1).getTime())
it 'should parse dates', ->
Morris.parseDate('2012-09-15').should.equal(new Date(2012, 8, 15).getTime())
Morris.parseDate('2012-10-15').should.equal(new Date(2012, 9, 15).getTime())
it 'should parse times', ->
Morris.parseDate("2012-10-15 12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
Morris.parseDate("2012-10-15T12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
Morris.parseDate("2012-10-15 12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
Morris.parseDate("2012-10-15T12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
it 'should parse times with timezones', ->
Morris.parseDate("2012-10-15T12:34+0100").should.equal(Date.UTC(2012, 9, 15, 11, 34))
Morris.parseDate("2012-10-15T12:34+02:00").should.equal(Date.UTC(2012, 9, 15, 10, 34))
Morris.parseDate("2012-10-15T12:34-0100").should.equal(Date.UTC(2012, 9, 15, 13, 34))
Morris.parseDate("2012-10-15T12:34-02:00").should.equal(Date.UTC(2012, 9, 15, 14, 34))
Morris.parseDate("2012-10-15T12:34:55Z").should.equal(Date.UTC(2012, 9, 15, 12, 34, 55))
Morris.parseDate("2012-10-15T12:34:55+0600").should.equal(Date.UTC(2012, 9, 15, 6, 34, 55))
Morris.parseDate("2012-10-15T12:34:55+04:00").should.equal(Date.UTC(2012, 9, 15, 8, 34, 55))
Morris.parseDate("2012-10-15T12:34:55-0600").should.equal(Date.UTC(2012, 9, 15, 18, 34, 55))
it 'should pass-through timestamps', ->
Morris.parseDate(new Date(2012, 9, 15, 12, 34, 55, 123).getTime())
.should.equal(new Date(2012, 9, 15, 12, 34, 55, 123).getTime())